feat(rules): add comprehensive documentation for Coolify architecture and development practices for AI tools, especially for cursor
- Introduced multiple new markdown files covering API and routing, application architecture, deployment architecture, database patterns, frontend patterns, and security practices. - Established guidelines for development workflows, testing strategies, and continuous improvement of rules. - Enhanced project overview and technology stack documentation to provide clarity on Coolify's features and architecture.
This commit is contained in:
292
.cursor/rules/README.mdc
Normal file
292
.cursor/rules/README.mdc
Normal file
@@ -0,0 +1,292 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Coolify Cursor Rules - Complete Guide
|
||||
|
||||
## Overview
|
||||
|
||||
This comprehensive set of Cursor Rules provides deep insights into **Coolify**, an open-source self-hostable alternative to Heroku/Netlify/Vercel. These rules will help you understand, navigate, and contribute to this complex Laravel-based deployment platform.
|
||||
|
||||
## Rule Categories
|
||||
|
||||
### 🏗️ Architecture & Foundation
|
||||
- **[project-overview.mdc](mdc:.cursor/rules/project-overview.mdc)** - What Coolify is and its core mission
|
||||
- **[technology-stack.mdc](mdc:.cursor/rules/technology-stack.mdc)** - Complete technology stack and dependencies
|
||||
- **[application-architecture.mdc](mdc:.cursor/rules/application-architecture.mdc)** - Laravel application structure and patterns
|
||||
|
||||
### 🎨 Frontend Development
|
||||
- **[frontend-patterns.mdc](mdc:.cursor/rules/frontend-patterns.mdc)** - Livewire + Alpine.js + Tailwind architecture
|
||||
|
||||
### 🗄️ Data & Backend
|
||||
- **[database-patterns.mdc](mdc:.cursor/rules/database-patterns.mdc)** - Database architecture, models, and data management
|
||||
- **[deployment-architecture.mdc](mdc:.cursor/rules/deployment-architecture.mdc)** - Docker orchestration and deployment workflows
|
||||
|
||||
### 🌐 API & Communication
|
||||
- **[api-and-routing.mdc](mdc:.cursor/rules/api-and-routing.mdc)** - RESTful APIs, webhooks, and routing patterns
|
||||
|
||||
### 🧪 Quality Assurance
|
||||
- **[testing-patterns.mdc](mdc:.cursor/rules/testing-patterns.mdc)** - Testing strategies with Pest PHP and Laravel Dusk
|
||||
|
||||
### 🔧 Development Process
|
||||
- **[development-workflow.mdc](mdc:.cursor/rules/development-workflow.mdc)** - Development setup, coding standards, and contribution guidelines
|
||||
|
||||
### 🔒 Security
|
||||
- **[security-patterns.mdc](mdc:.cursor/rules/security-patterns.mdc)** - Security architecture, authentication, and best practices
|
||||
|
||||
## Quick Navigation
|
||||
|
||||
### Core Application Files
|
||||
- **[app/Models/Application.php](mdc:app/Models/Application.php)** - Main application entity (74KB, highly complex)
|
||||
- **[app/Models/Server.php](mdc:app/Models/Server.php)** - Server management (46KB, complex)
|
||||
- **[app/Models/Service.php](mdc:app/Models/Service.php)** - Service definitions (58KB, complex)
|
||||
- **[app/Models/Team.php](mdc:app/Models/Team.php)** - Multi-tenant structure (8.9KB)
|
||||
|
||||
### Configuration Files
|
||||
- **[composer.json](mdc:composer.json)** - PHP dependencies and Laravel setup
|
||||
- **[package.json](mdc:package.json)** - Frontend dependencies and build scripts
|
||||
- **[vite.config.js](mdc:vite.config.js)** - Frontend build configuration
|
||||
- **[docker-compose.dev.yml](mdc:docker-compose.dev.yml)** - Development environment
|
||||
|
||||
### API Documentation
|
||||
- **[openapi.json](mdc:openapi.json)** - Complete API documentation (373KB)
|
||||
- **[routes/api.php](mdc:routes/api.php)** - API endpoint definitions (13KB)
|
||||
- **[routes/web.php](mdc:routes/web.php)** - Web application routes (21KB)
|
||||
|
||||
## Key Concepts to Understand
|
||||
|
||||
### 1. Multi-Tenant Architecture
|
||||
Coolify uses a **team-based multi-tenancy** model where:
|
||||
- Users belong to multiple teams
|
||||
- Resources are scoped to teams
|
||||
- Access control is team-based
|
||||
- Data isolation is enforced at the database level
|
||||
|
||||
### 2. Deployment Philosophy
|
||||
- **Docker-first** approach for all deployments
|
||||
- **Zero-downtime** deployments with health checks
|
||||
- **Git-based** workflows with webhook integration
|
||||
- **Multi-server** support with SSH connections
|
||||
|
||||
### 3. Technology Stack
|
||||
- **Backend**: Laravel 11 + PHP 8.4
|
||||
- **Frontend**: Livewire 3.5 + Alpine.js + Tailwind CSS 4.1
|
||||
- **Database**: PostgreSQL 15 + Redis 7
|
||||
- **Containerization**: Docker + Docker Compose
|
||||
- **Testing**: Pest PHP 3.8 + Laravel Dusk
|
||||
|
||||
### 4. Security Model
|
||||
- **Defense-in-depth** security architecture
|
||||
- **OAuth integration** with multiple providers
|
||||
- **API token** authentication with Sanctum
|
||||
- **Encrypted storage** for sensitive data
|
||||
- **SSH key** management for server access
|
||||
|
||||
## Development Quick Start
|
||||
|
||||
### Local Setup
|
||||
```bash
|
||||
# Clone and setup
|
||||
git clone https://github.com/coollabsio/coolify.git
|
||||
cd coolify
|
||||
cp .env.example .env
|
||||
|
||||
# Docker development (recommended)
|
||||
docker-compose -f docker-compose.dev.yml up -d
|
||||
docker-compose exec app composer install
|
||||
docker-compose exec app npm install
|
||||
docker-compose exec app php artisan migrate
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
```bash
|
||||
# PHP code style
|
||||
./vendor/bin/pint
|
||||
|
||||
# Static analysis
|
||||
./vendor/bin/phpstan analyse
|
||||
|
||||
# Run tests
|
||||
./vendor/bin/pest
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Livewire Components
|
||||
```php
|
||||
class ApplicationShow extends Component
|
||||
{
|
||||
public Application $application;
|
||||
|
||||
protected $listeners = [
|
||||
'deployment.started' => 'refresh',
|
||||
'deployment.completed' => 'refresh',
|
||||
];
|
||||
|
||||
public function deploy(): void
|
||||
{
|
||||
$this->authorize('deploy', $this->application);
|
||||
app(ApplicationDeploymentService::class)->deploy($this->application);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### API Controllers
|
||||
```php
|
||||
class ApplicationController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth:sanctum');
|
||||
$this->middleware('team.access');
|
||||
}
|
||||
|
||||
public function deploy(Application $application): JsonResponse
|
||||
{
|
||||
$this->authorize('deploy', $application);
|
||||
$deployment = app(ApplicationDeploymentService::class)->deploy($application);
|
||||
return response()->json(['deployment_id' => $deployment->id]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Queue Jobs
|
||||
```php
|
||||
class DeployApplicationJob implements ShouldQueue
|
||||
{
|
||||
public function handle(DockerService $dockerService): void
|
||||
{
|
||||
$this->deployment->update(['status' => 'running']);
|
||||
|
||||
try {
|
||||
$dockerService->deployContainer($this->deployment->application);
|
||||
$this->deployment->update(['status' => 'success']);
|
||||
} catch (Exception $e) {
|
||||
$this->deployment->update(['status' => 'failed']);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
### Feature Tests
|
||||
```php
|
||||
test('user can deploy application via API', function () {
|
||||
$user = User::factory()->create();
|
||||
$application = Application::factory()->create(['team_id' => $user->currentTeam->id]);
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->postJson("/api/v1/applications/{$application->id}/deploy");
|
||||
|
||||
$response->assertStatus(200);
|
||||
expect($application->deployments()->count())->toBe(1);
|
||||
});
|
||||
```
|
||||
|
||||
### Browser Tests
|
||||
```php
|
||||
test('user can create application through UI', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->browse(function (Browser $browser) use ($user) {
|
||||
$browser->loginAs($user)
|
||||
->visit('/applications/create')
|
||||
->type('name', 'Test App')
|
||||
->press('Create Application')
|
||||
->assertSee('Application created successfully');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Authentication
|
||||
- Multi-provider OAuth support
|
||||
- API token authentication
|
||||
- Team-based access control
|
||||
- Session management
|
||||
|
||||
### Data Protection
|
||||
- Encrypted environment variables
|
||||
- Secure SSH key storage
|
||||
- Input validation and sanitization
|
||||
- SQL injection prevention
|
||||
|
||||
### Container Security
|
||||
- Non-root container users
|
||||
- Minimal capabilities
|
||||
- Read-only filesystems
|
||||
- Network isolation
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Database
|
||||
- Eager loading relationships
|
||||
- Query optimization
|
||||
- Connection pooling
|
||||
- Caching strategies
|
||||
|
||||
### Frontend
|
||||
- Lazy loading components
|
||||
- Asset optimization
|
||||
- CDN integration
|
||||
- Real-time updates via WebSockets
|
||||
|
||||
## Contributing Guidelines
|
||||
|
||||
### Code Standards
|
||||
- PSR-12 PHP coding standards
|
||||
- Laravel best practices
|
||||
- Comprehensive test coverage
|
||||
- Security-first approach
|
||||
|
||||
### Pull Request Process
|
||||
1. Fork repository
|
||||
2. Create feature branch
|
||||
3. Implement with tests
|
||||
4. Run quality checks
|
||||
5. Submit PR with clear description
|
||||
|
||||
## Useful Commands
|
||||
|
||||
### Development
|
||||
```bash
|
||||
# Start development environment
|
||||
docker-compose -f docker-compose.dev.yml up -d
|
||||
|
||||
# Run tests
|
||||
./vendor/bin/pest
|
||||
|
||||
# Code formatting
|
||||
./vendor/bin/pint
|
||||
|
||||
# Frontend development
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Production
|
||||
```bash
|
||||
# Install Coolify
|
||||
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
||||
|
||||
# Update Coolify
|
||||
./scripts/upgrade.sh
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
### Documentation
|
||||
- **[README.md](mdc:README.md)** - Project overview and installation
|
||||
- **[CONTRIBUTING.md](mdc:CONTRIBUTING.md)** - Contribution guidelines
|
||||
- **[CHANGELOG.md](mdc:CHANGELOG.md)** - Release history
|
||||
- **[TECH_STACK.md](mdc:TECH_STACK.md)** - Technology overview
|
||||
|
||||
### Configuration
|
||||
- **[config/](mdc:config)** - Laravel configuration files
|
||||
- **[database/migrations/](mdc:database/migrations)** - Database schema
|
||||
- **[tests/](mdc:tests)** - Test suite
|
||||
|
||||
This comprehensive rule set provides everything needed to understand, develop, and contribute to the Coolify project effectively. Each rule focuses on specific aspects while maintaining connections to the broader architecture.
|
474
.cursor/rules/api-and-routing.mdc
Normal file
474
.cursor/rules/api-and-routing.mdc
Normal file
@@ -0,0 +1,474 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Coolify API & Routing Architecture
|
||||
|
||||
## Routing Structure
|
||||
|
||||
Coolify implements **multi-layered routing** with web interfaces, RESTful APIs, webhook endpoints, and real-time communication channels.
|
||||
|
||||
## Route Files
|
||||
|
||||
### Core Route Definitions
|
||||
- **[routes/web.php](mdc:routes/web.php)** - Web application routes (21KB, 362 lines)
|
||||
- **[routes/api.php](mdc:routes/api.php)** - RESTful API endpoints (13KB, 185 lines)
|
||||
- **[routes/webhooks.php](mdc:routes/webhooks.php)** - Webhook receivers (815B, 22 lines)
|
||||
- **[routes/channels.php](mdc:routes/channels.php)** - WebSocket channel definitions (829B, 33 lines)
|
||||
- **[routes/console.php](mdc:routes/console.php)** - Artisan command routes (592B, 20 lines)
|
||||
|
||||
## Web Application Routing
|
||||
|
||||
### Authentication Routes
|
||||
```php
|
||||
// Laravel Fortify authentication
|
||||
Route::middleware('guest')->group(function () {
|
||||
Route::get('/login', [AuthController::class, 'login']);
|
||||
Route::get('/register', [AuthController::class, 'register']);
|
||||
Route::get('/forgot-password', [AuthController::class, 'forgotPassword']);
|
||||
});
|
||||
```
|
||||
|
||||
### Dashboard & Core Features
|
||||
```php
|
||||
// Main application routes
|
||||
Route::middleware(['auth', 'verified'])->group(function () {
|
||||
Route::get('/dashboard', Dashboard::class)->name('dashboard');
|
||||
Route::get('/projects', ProjectIndex::class)->name('projects');
|
||||
Route::get('/servers', ServerIndex::class)->name('servers');
|
||||
Route::get('/teams', TeamIndex::class)->name('teams');
|
||||
});
|
||||
```
|
||||
|
||||
### Resource Management Routes
|
||||
```php
|
||||
// Server management
|
||||
Route::prefix('servers')->group(function () {
|
||||
Route::get('/{server}', ServerShow::class)->name('server.show');
|
||||
Route::get('/{server}/edit', ServerEdit::class)->name('server.edit');
|
||||
Route::get('/{server}/logs', ServerLogs::class)->name('server.logs');
|
||||
});
|
||||
|
||||
// Application management
|
||||
Route::prefix('applications')->group(function () {
|
||||
Route::get('/{application}', ApplicationShow::class)->name('application.show');
|
||||
Route::get('/{application}/deployments', ApplicationDeployments::class);
|
||||
Route::get('/{application}/environment-variables', ApplicationEnvironmentVariables::class);
|
||||
Route::get('/{application}/logs', ApplicationLogs::class);
|
||||
});
|
||||
```
|
||||
|
||||
## RESTful API Architecture
|
||||
|
||||
### API Versioning
|
||||
```php
|
||||
// API route structure
|
||||
Route::prefix('v1')->group(function () {
|
||||
// Application endpoints
|
||||
Route::apiResource('applications', ApplicationController::class);
|
||||
Route::apiResource('servers', ServerController::class);
|
||||
Route::apiResource('teams', TeamController::class);
|
||||
});
|
||||
```
|
||||
|
||||
### Authentication & Authorization
|
||||
```php
|
||||
// Sanctum API authentication
|
||||
Route::middleware('auth:sanctum')->group(function () {
|
||||
Route::get('/user', function (Request $request) {
|
||||
return $request->user();
|
||||
});
|
||||
|
||||
// Team-scoped resources
|
||||
Route::middleware('team.access')->group(function () {
|
||||
Route::apiResource('applications', ApplicationController::class);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Application Management API
|
||||
```php
|
||||
// Application CRUD operations
|
||||
Route::prefix('applications')->group(function () {
|
||||
Route::get('/', [ApplicationController::class, 'index']);
|
||||
Route::post('/', [ApplicationController::class, 'store']);
|
||||
Route::get('/{application}', [ApplicationController::class, 'show']);
|
||||
Route::patch('/{application}', [ApplicationController::class, 'update']);
|
||||
Route::delete('/{application}', [ApplicationController::class, 'destroy']);
|
||||
|
||||
// Deployment operations
|
||||
Route::post('/{application}/deploy', [ApplicationController::class, 'deploy']);
|
||||
Route::post('/{application}/restart', [ApplicationController::class, 'restart']);
|
||||
Route::post('/{application}/stop', [ApplicationController::class, 'stop']);
|
||||
Route::get('/{application}/logs', [ApplicationController::class, 'logs']);
|
||||
});
|
||||
```
|
||||
|
||||
### Server Management API
|
||||
```php
|
||||
// Server operations
|
||||
Route::prefix('servers')->group(function () {
|
||||
Route::get('/', [ServerController::class, 'index']);
|
||||
Route::post('/', [ServerController::class, 'store']);
|
||||
Route::get('/{server}', [ServerController::class, 'show']);
|
||||
Route::patch('/{server}', [ServerController::class, 'update']);
|
||||
Route::delete('/{server}', [ServerController::class, 'destroy']);
|
||||
|
||||
// Server actions
|
||||
Route::post('/{server}/validate', [ServerController::class, 'validate']);
|
||||
Route::get('/{server}/usage', [ServerController::class, 'usage']);
|
||||
Route::post('/{server}/cleanup', [ServerController::class, 'cleanup']);
|
||||
});
|
||||
```
|
||||
|
||||
### Database Management API
|
||||
```php
|
||||
// Database operations
|
||||
Route::prefix('databases')->group(function () {
|
||||
Route::get('/', [DatabaseController::class, 'index']);
|
||||
Route::post('/', [DatabaseController::class, 'store']);
|
||||
Route::get('/{database}', [DatabaseController::class, 'show']);
|
||||
Route::patch('/{database}', [DatabaseController::class, 'update']);
|
||||
Route::delete('/{database}', [DatabaseController::class, 'destroy']);
|
||||
|
||||
// Database actions
|
||||
Route::post('/{database}/backup', [DatabaseController::class, 'backup']);
|
||||
Route::post('/{database}/restore', [DatabaseController::class, 'restore']);
|
||||
Route::get('/{database}/logs', [DatabaseController::class, 'logs']);
|
||||
});
|
||||
```
|
||||
|
||||
## Webhook Architecture
|
||||
|
||||
### Git Integration Webhooks
|
||||
```php
|
||||
// GitHub webhook endpoints
|
||||
Route::post('/webhooks/github/{application}', [GitHubWebhookController::class, 'handle'])
|
||||
->name('webhooks.github');
|
||||
|
||||
// GitLab webhook endpoints
|
||||
Route::post('/webhooks/gitlab/{application}', [GitLabWebhookController::class, 'handle'])
|
||||
->name('webhooks.gitlab');
|
||||
|
||||
// Generic Git webhooks
|
||||
Route::post('/webhooks/git/{application}', [GitWebhookController::class, 'handle'])
|
||||
->name('webhooks.git');
|
||||
```
|
||||
|
||||
### Deployment Webhooks
|
||||
```php
|
||||
// Deployment status webhooks
|
||||
Route::post('/webhooks/deployment/{deployment}/success', [DeploymentWebhookController::class, 'success']);
|
||||
Route::post('/webhooks/deployment/{deployment}/failure', [DeploymentWebhookController::class, 'failure']);
|
||||
Route::post('/webhooks/deployment/{deployment}/progress', [DeploymentWebhookController::class, 'progress']);
|
||||
```
|
||||
|
||||
### Third-Party Integration Webhooks
|
||||
```php
|
||||
// Monitoring webhooks
|
||||
Route::post('/webhooks/monitoring/{server}', [MonitoringWebhookController::class, 'handle']);
|
||||
|
||||
// Backup status webhooks
|
||||
Route::post('/webhooks/backup/{backup}', [BackupWebhookController::class, 'handle']);
|
||||
|
||||
// SSL certificate webhooks
|
||||
Route::post('/webhooks/ssl/{certificate}', [SslWebhookController::class, 'handle']);
|
||||
```
|
||||
|
||||
## WebSocket Channel Definitions
|
||||
|
||||
### Real-Time Channels
|
||||
```php
|
||||
// Private channels for team members
|
||||
Broadcast::channel('team.{teamId}', function ($user, $teamId) {
|
||||
return $user->teams->contains('id', $teamId);
|
||||
});
|
||||
|
||||
// Application deployment channels
|
||||
Broadcast::channel('application.{applicationId}', function ($user, $applicationId) {
|
||||
return $user->hasAccessToApplication($applicationId);
|
||||
});
|
||||
|
||||
// Server monitoring channels
|
||||
Broadcast::channel('server.{serverId}', function ($user, $serverId) {
|
||||
return $user->hasAccessToServer($serverId);
|
||||
});
|
||||
```
|
||||
|
||||
### Presence Channels
|
||||
```php
|
||||
// Team collaboration presence
|
||||
Broadcast::channel('team.{teamId}.presence', function ($user, $teamId) {
|
||||
if ($user->teams->contains('id', $teamId)) {
|
||||
return ['id' => $user->id, 'name' => $user->name];
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## API Controllers
|
||||
|
||||
### Location: [app/Http/Controllers/Api/](mdc:app/Http/Controllers)
|
||||
|
||||
#### Resource Controllers
|
||||
```php
|
||||
class ApplicationController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
return ApplicationResource::collection(
|
||||
$request->user()->currentTeam->applications()
|
||||
->with(['server', 'environment'])
|
||||
->paginate()
|
||||
);
|
||||
}
|
||||
|
||||
public function store(StoreApplicationRequest $request)
|
||||
{
|
||||
$application = $request->user()->currentTeam
|
||||
->applications()
|
||||
->create($request->validated());
|
||||
|
||||
return new ApplicationResource($application);
|
||||
}
|
||||
|
||||
public function deploy(Application $application)
|
||||
{
|
||||
$deployment = $application->deploy();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Deployment started',
|
||||
'deployment_id' => $deployment->id
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### API Responses & Resources
|
||||
```php
|
||||
// API Resource classes
|
||||
class ApplicationResource extends JsonResource
|
||||
{
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'fqdn' => $this->fqdn,
|
||||
'status' => $this->status,
|
||||
'git_repository' => $this->git_repository,
|
||||
'git_branch' => $this->git_branch,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
'server' => new ServerResource($this->whenLoaded('server')),
|
||||
'environment' => new EnvironmentResource($this->whenLoaded('environment')),
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Authentication
|
||||
|
||||
### Sanctum Token Authentication
|
||||
```php
|
||||
// API token generation
|
||||
Route::post('/auth/tokens', function (Request $request) {
|
||||
$request->validate([
|
||||
'name' => 'required|string',
|
||||
'abilities' => 'array'
|
||||
]);
|
||||
|
||||
$token = $request->user()->createToken(
|
||||
$request->name,
|
||||
$request->abilities ?? []
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'token' => $token->plainTextToken,
|
||||
'abilities' => $token->accessToken->abilities
|
||||
]);
|
||||
});
|
||||
```
|
||||
|
||||
### Team-Based Authorization
|
||||
```php
|
||||
// Team access middleware
|
||||
class EnsureTeamAccess
|
||||
{
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$teamId = $request->route('team');
|
||||
|
||||
if (!$request->user()->teams->contains('id', $teamId)) {
|
||||
abort(403, 'Access denied to team resources');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
### API Rate Limits
|
||||
```php
|
||||
// API throttling configuration
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
|
||||
});
|
||||
|
||||
// Deployment rate limiting
|
||||
RateLimiter::for('deployments', function (Request $request) {
|
||||
return Limit::perMinute(10)->by($request->user()->id);
|
||||
});
|
||||
```
|
||||
|
||||
### Webhook Rate Limiting
|
||||
```php
|
||||
// Webhook throttling
|
||||
RateLimiter::for('webhooks', function (Request $request) {
|
||||
return Limit::perMinute(100)->by($request->ip());
|
||||
});
|
||||
```
|
||||
|
||||
## Route Model Binding
|
||||
|
||||
### Custom Route Bindings
|
||||
```php
|
||||
// Custom model binding for applications
|
||||
Route::bind('application', function ($value) {
|
||||
return Application::where('uuid', $value)
|
||||
->orWhere('id', $value)
|
||||
->firstOrFail();
|
||||
});
|
||||
|
||||
// Team-scoped model binding
|
||||
Route::bind('team_application', function ($value, $route) {
|
||||
$teamId = $route->parameter('team');
|
||||
return Application::whereHas('environment.project', function ($query) use ($teamId) {
|
||||
$query->where('team_id', $teamId);
|
||||
})->findOrFail($value);
|
||||
});
|
||||
```
|
||||
|
||||
## API Documentation
|
||||
|
||||
### OpenAPI Specification
|
||||
- **[openapi.json](mdc:openapi.json)** - API documentation (373KB, 8316 lines)
|
||||
- **[openapi.yaml](mdc:openapi.yaml)** - YAML format documentation (184KB, 5579 lines)
|
||||
|
||||
### Documentation Generation
|
||||
```php
|
||||
// Swagger/OpenAPI annotations
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/applications",
|
||||
* summary="List applications",
|
||||
* tags={"Applications"},
|
||||
* security={{"bearerAuth":{}}},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="List of applications",
|
||||
* @OA\JsonContent(type="array", @OA\Items(ref="#/components/schemas/Application"))
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### API Error Responses
|
||||
```php
|
||||
// Standardized error response format
|
||||
class ApiExceptionHandler
|
||||
{
|
||||
public function render($request, Throwable $exception)
|
||||
{
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json([
|
||||
'message' => $exception->getMessage(),
|
||||
'error_code' => $this->getErrorCode($exception),
|
||||
'timestamp' => now()->toISOString()
|
||||
], $this->getStatusCode($exception));
|
||||
}
|
||||
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Validation Error Handling
|
||||
```php
|
||||
// Form request validation
|
||||
class StoreApplicationRequest extends FormRequest
|
||||
{
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'git_repository' => 'required|url',
|
||||
'git_branch' => 'required|string',
|
||||
'server_id' => 'required|exists:servers,id',
|
||||
'environment_id' => 'required|exists:environments,id'
|
||||
];
|
||||
}
|
||||
|
||||
public function failedValidation(Validator $validator)
|
||||
{
|
||||
throw new HttpResponseException(
|
||||
response()->json([
|
||||
'message' => 'Validation failed',
|
||||
'errors' => $validator->errors()
|
||||
], 422)
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Real-Time API Integration
|
||||
|
||||
### WebSocket Events
|
||||
```php
|
||||
// Broadcasting deployment events
|
||||
class DeploymentStarted implements ShouldBroadcast
|
||||
{
|
||||
public $application;
|
||||
public $deployment;
|
||||
|
||||
public function broadcastOn()
|
||||
{
|
||||
return [
|
||||
new PrivateChannel("application.{$this->application->id}"),
|
||||
new PrivateChannel("team.{$this->application->team->id}")
|
||||
];
|
||||
}
|
||||
|
||||
public function broadcastWith()
|
||||
{
|
||||
return [
|
||||
'deployment_id' => $this->deployment->id,
|
||||
'status' => 'started',
|
||||
'timestamp' => now()
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### API Event Streaming
|
||||
```php
|
||||
// Server-Sent Events for real-time updates
|
||||
Route::get('/api/v1/applications/{application}/events', function (Application $application) {
|
||||
return response()->stream(function () use ($application) {
|
||||
while (true) {
|
||||
$events = $application->getRecentEvents();
|
||||
foreach ($events as $event) {
|
||||
echo "data: " . json_encode($event) . "\n\n";
|
||||
}
|
||||
usleep(1000000); // 1 second
|
||||
}
|
||||
}, 200, [
|
||||
'Content-Type' => 'text/event-stream',
|
||||
'Cache-Control' => 'no-cache',
|
||||
]);
|
||||
});
|
||||
```
|
368
.cursor/rules/application-architecture.mdc
Normal file
368
.cursor/rules/application-architecture.mdc
Normal file
@@ -0,0 +1,368 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Coolify Application Architecture
|
||||
|
||||
## Laravel Project Structure
|
||||
|
||||
### **Core Application Directory** ([app/](mdc:app))
|
||||
|
||||
```
|
||||
app/
|
||||
├── Actions/ # Business logic actions (Action pattern)
|
||||
├── Console/ # Artisan commands
|
||||
├── Contracts/ # Interface definitions
|
||||
├── Data/ # Data Transfer Objects (Spatie Laravel Data)
|
||||
├── Enums/ # Enumeration classes
|
||||
├── Events/ # Event classes
|
||||
├── Exceptions/ # Custom exception classes
|
||||
├── Helpers/ # Utility helper classes
|
||||
├── Http/ # HTTP layer (Controllers, Middleware, Requests)
|
||||
├── Jobs/ # Background job classes
|
||||
├── Listeners/ # Event listeners
|
||||
├── Livewire/ # Livewire components (Frontend)
|
||||
├── Models/ # Eloquent models (Domain entities)
|
||||
├── Notifications/ # Notification classes
|
||||
├── Policies/ # Authorization policies
|
||||
├── Providers/ # Service providers
|
||||
├── Repositories/ # Repository pattern implementations
|
||||
├── Services/ # Service layer classes
|
||||
├── Traits/ # Reusable trait classes
|
||||
└── View/ # View composers and creators
|
||||
```
|
||||
|
||||
## Core Domain Models
|
||||
|
||||
### **Infrastructure Management**
|
||||
|
||||
#### **[Server.php](mdc:app/Models/Server.php)** (46KB, 1343 lines)
|
||||
- **Purpose**: Physical/virtual server management
|
||||
- **Key Relationships**:
|
||||
- `hasMany(Application::class)` - Deployed applications
|
||||
- `hasMany(StandalonePostgresql::class)` - Database instances
|
||||
- `belongsTo(Team::class)` - Team ownership
|
||||
- **Key Features**:
|
||||
- SSH connection management
|
||||
- Resource monitoring
|
||||
- Proxy configuration (Traefik/Caddy)
|
||||
- Docker daemon interaction
|
||||
|
||||
#### **[Application.php](mdc:app/Models/Application.php)** (74KB, 1734 lines)
|
||||
- **Purpose**: Application deployment and management
|
||||
- **Key Relationships**:
|
||||
- `belongsTo(Server::class)` - Deployment target
|
||||
- `belongsTo(Environment::class)` - Environment context
|
||||
- `hasMany(ApplicationDeploymentQueue::class)` - Deployment history
|
||||
- **Key Features**:
|
||||
- Git repository integration
|
||||
- Docker build and deployment
|
||||
- Environment variable management
|
||||
- SSL certificate handling
|
||||
|
||||
#### **[Service.php](mdc:app/Models/Service.php)** (58KB, 1325 lines)
|
||||
- **Purpose**: Multi-container service orchestration
|
||||
- **Key Relationships**:
|
||||
- `hasMany(ServiceApplication::class)` - Service components
|
||||
- `hasMany(ServiceDatabase::class)` - Service databases
|
||||
- `belongsTo(Environment::class)` - Environment context
|
||||
- **Key Features**:
|
||||
- Docker Compose generation
|
||||
- Service dependency management
|
||||
- Health check configuration
|
||||
|
||||
### **Team & Project Organization**
|
||||
|
||||
#### **[Team.php](mdc:app/Models/Team.php)** (8.9KB, 308 lines)
|
||||
- **Purpose**: Multi-tenant team management
|
||||
- **Key Relationships**:
|
||||
- `hasMany(User::class)` - Team members
|
||||
- `hasMany(Project::class)` - Team projects
|
||||
- `hasMany(Server::class)` - Team servers
|
||||
- **Key Features**:
|
||||
- Resource limits and quotas
|
||||
- Team-based access control
|
||||
- Subscription management
|
||||
|
||||
#### **[Project.php](mdc:app/Models/Project.php)** (4.3KB, 156 lines)
|
||||
- **Purpose**: Project organization and grouping
|
||||
- **Key Relationships**:
|
||||
- `hasMany(Environment::class)` - Project environments
|
||||
- `belongsTo(Team::class)` - Team ownership
|
||||
- **Key Features**:
|
||||
- Environment isolation
|
||||
- Resource organization
|
||||
|
||||
#### **[Environment.php](mdc:app/Models/Environment.php)**
|
||||
- **Purpose**: Environment-specific configuration
|
||||
- **Key Relationships**:
|
||||
- `hasMany(Application::class)` - Environment applications
|
||||
- `hasMany(Service::class)` - Environment services
|
||||
- `belongsTo(Project::class)` - Project context
|
||||
|
||||
### **Database Management Models**
|
||||
|
||||
#### **Standalone Database Models**
|
||||
- **[StandalonePostgresql.php](mdc:app/Models/StandalonePostgresql.php)** (11KB, 351 lines)
|
||||
- **[StandaloneMysql.php](mdc:app/Models/StandaloneMysql.php)** (11KB, 351 lines)
|
||||
- **[StandaloneMariadb.php](mdc:app/Models/StandaloneMariadb.php)** (10KB, 337 lines)
|
||||
- **[StandaloneMongodb.php](mdc:app/Models/StandaloneMongodb.php)** (12KB, 370 lines)
|
||||
- **[StandaloneRedis.php](mdc:app/Models/StandaloneRedis.php)** (12KB, 394 lines)
|
||||
- **[StandaloneKeydb.php](mdc:app/Models/StandaloneKeydb.php)** (11KB, 347 lines)
|
||||
- **[StandaloneDragonfly.php](mdc:app/Models/StandaloneDragonfly.php)** (11KB, 347 lines)
|
||||
- **[StandaloneClickhouse.php](mdc:app/Models/StandaloneClickhouse.php)** (10KB, 336 lines)
|
||||
|
||||
**Common Features**:
|
||||
- Database configuration management
|
||||
- Backup scheduling and execution
|
||||
- Connection string generation
|
||||
- Health monitoring
|
||||
|
||||
### **Configuration & Settings**
|
||||
|
||||
#### **[EnvironmentVariable.php](mdc:app/Models/EnvironmentVariable.php)** (7.6KB, 219 lines)
|
||||
- **Purpose**: Application environment variable management
|
||||
- **Key Features**:
|
||||
- Encrypted value storage
|
||||
- Build-time vs runtime variables
|
||||
- Shared variable inheritance
|
||||
|
||||
#### **[InstanceSettings.php](mdc:app/Models/InstanceSettings.php)** (3.2KB, 124 lines)
|
||||
- **Purpose**: Global Coolify instance configuration
|
||||
- **Key Features**:
|
||||
- FQDN and port configuration
|
||||
- Auto-update settings
|
||||
- Security configurations
|
||||
|
||||
## Architectural Patterns
|
||||
|
||||
### **Action Pattern** ([app/Actions/](mdc:app/Actions))
|
||||
|
||||
Using [lorisleiva/laravel-actions](mdc:composer.json) for business logic encapsulation:
|
||||
|
||||
```php
|
||||
// Example Action structure
|
||||
class DeployApplication extends Action
|
||||
{
|
||||
public function handle(Application $application): void
|
||||
{
|
||||
// Business logic for deployment
|
||||
}
|
||||
|
||||
public function asJob(Application $application): void
|
||||
{
|
||||
// Queue job implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key Action Categories**:
|
||||
- **Application/**: Deployment and management actions
|
||||
- **Database/**: Database operations
|
||||
- **Server/**: Server management actions
|
||||
- **Service/**: Service orchestration actions
|
||||
|
||||
### **Repository Pattern** ([app/Repositories/](mdc:app/Repositories))
|
||||
|
||||
Data access abstraction layer:
|
||||
- Encapsulates database queries
|
||||
- Provides testable data layer
|
||||
- Abstracts complex query logic
|
||||
|
||||
### **Service Layer** ([app/Services/](mdc:app/Services))
|
||||
|
||||
Business logic services:
|
||||
- External API integrations
|
||||
- Complex business operations
|
||||
- Cross-cutting concerns
|
||||
|
||||
## Data Flow Architecture
|
||||
|
||||
### **Request Lifecycle**
|
||||
|
||||
1. **HTTP Request** → [routes/web.php](mdc:routes/web.php)
|
||||
2. **Middleware** → Authentication, authorization
|
||||
3. **Livewire Component** → [app/Livewire/](mdc:app/Livewire)
|
||||
4. **Action/Service** → Business logic execution
|
||||
5. **Model/Repository** → Data persistence
|
||||
6. **Response** → Livewire reactive update
|
||||
|
||||
### **Background Processing**
|
||||
|
||||
1. **Job Dispatch** → Queue system (Redis)
|
||||
2. **Job Processing** → [app/Jobs/](mdc:app/Jobs)
|
||||
3. **Action Execution** → Business logic
|
||||
4. **Event Broadcasting** → Real-time updates
|
||||
5. **Notification** → User feedback
|
||||
|
||||
## Security Architecture
|
||||
|
||||
### **Multi-Tenant Isolation**
|
||||
|
||||
```php
|
||||
// Team-based query scoping
|
||||
class Application extends Model
|
||||
{
|
||||
public function scopeOwnedByCurrentTeam($query)
|
||||
{
|
||||
return $query->whereHas('environment.project.team', function ($q) {
|
||||
$q->where('id', currentTeam()->id);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Authorization Layers**
|
||||
|
||||
1. **Team Membership** → User belongs to team
|
||||
2. **Resource Ownership** → Resource belongs to team
|
||||
3. **Policy Authorization** → [app/Policies/](mdc:app/Policies)
|
||||
4. **Environment Isolation** → Project/environment boundaries
|
||||
|
||||
### **Data Protection**
|
||||
|
||||
- **Environment Variables**: Encrypted at rest
|
||||
- **SSH Keys**: Secure storage and transmission
|
||||
- **API Tokens**: Sanctum-based authentication
|
||||
- **Audit Logging**: [spatie/laravel-activitylog](mdc:composer.json)
|
||||
|
||||
## Configuration Hierarchy
|
||||
|
||||
### **Global Configuration**
|
||||
- **[InstanceSettings](mdc:app/Models/InstanceSettings.php)**: System-wide settings
|
||||
- **[config/](mdc:config)**: Laravel configuration files
|
||||
|
||||
### **Team Configuration**
|
||||
- **[Team](mdc:app/Models/Team.php)**: Team-specific settings
|
||||
- **[ServerSetting](mdc:app/Models/ServerSetting.php)**: Server configurations
|
||||
|
||||
### **Project Configuration**
|
||||
- **[ProjectSetting](mdc:app/Models/ProjectSetting.php)**: Project settings
|
||||
- **[Environment](mdc:app/Models/Environment.php)**: Environment variables
|
||||
|
||||
### **Application Configuration**
|
||||
- **[ApplicationSetting](mdc:app/Models/ApplicationSetting.php)**: App-specific settings
|
||||
- **[EnvironmentVariable](mdc:app/Models/EnvironmentVariable.php)**: Runtime configuration
|
||||
|
||||
## Event-Driven Architecture
|
||||
|
||||
### **Event Broadcasting** ([app/Events/](mdc:app/Events))
|
||||
|
||||
Real-time updates using Laravel Echo and WebSockets:
|
||||
|
||||
```php
|
||||
// Example event structure
|
||||
class ApplicationDeploymentStarted implements ShouldBroadcast
|
||||
{
|
||||
public function broadcastOn(): array
|
||||
{
|
||||
return [
|
||||
new PrivateChannel("team.{$this->application->team->id}"),
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Event Listeners** ([app/Listeners/](mdc:app/Listeners))
|
||||
|
||||
- Deployment status updates
|
||||
- Resource monitoring alerts
|
||||
- Notification dispatching
|
||||
- Audit log creation
|
||||
|
||||
## Database Design Patterns
|
||||
|
||||
### **Polymorphic Relationships**
|
||||
|
||||
```php
|
||||
// Environment variables can belong to multiple resource types
|
||||
class EnvironmentVariable extends Model
|
||||
{
|
||||
public function resource(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Team-Based Soft Scoping**
|
||||
|
||||
All major resources include team-based query scoping:
|
||||
|
||||
```php
|
||||
// Automatic team filtering
|
||||
$applications = Application::ownedByCurrentTeam()->get();
|
||||
$servers = Server::ownedByCurrentTeam()->get();
|
||||
```
|
||||
|
||||
### **Configuration Inheritance**
|
||||
|
||||
Environment variables cascade from:
|
||||
1. **Shared Variables** → Team-wide defaults
|
||||
2. **Project Variables** → Project-specific overrides
|
||||
3. **Application Variables** → Application-specific values
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
### **Git Provider Integration**
|
||||
|
||||
Abstracted git operations supporting:
|
||||
- **GitHub**: [app/Models/GithubApp.php](mdc:app/Models/GithubApp.php)
|
||||
- **GitLab**: [app/Models/GitlabApp.php](mdc:app/Models/GitlabApp.php)
|
||||
- **Bitbucket**: Webhook integration
|
||||
- **Gitea**: Self-hosted Git support
|
||||
|
||||
### **Docker Integration**
|
||||
|
||||
- **Container Management**: Direct Docker API communication
|
||||
- **Image Building**: Dockerfile and Buildpack support
|
||||
- **Network Management**: Custom Docker networks
|
||||
- **Volume Management**: Persistent storage handling
|
||||
|
||||
### **SSH Communication**
|
||||
|
||||
- **[phpseclib/phpseclib](mdc:composer.json)**: Secure SSH connections
|
||||
- **Multiplexing**: Connection pooling for efficiency
|
||||
- **Key Management**: [PrivateKey](mdc:app/Models/PrivateKey.php) model
|
||||
|
||||
## Testing Architecture
|
||||
|
||||
### **Test Structure** ([tests/](mdc:tests))
|
||||
|
||||
```
|
||||
tests/
|
||||
├── Feature/ # Integration tests
|
||||
├── Unit/ # Unit tests
|
||||
├── Browser/ # Dusk browser tests
|
||||
├── Traits/ # Test helper traits
|
||||
├── Pest.php # Pest configuration
|
||||
└── TestCase.php # Base test case
|
||||
```
|
||||
|
||||
### **Testing Patterns**
|
||||
|
||||
- **Feature Tests**: Full request lifecycle testing
|
||||
- **Unit Tests**: Individual class/method testing
|
||||
- **Browser Tests**: End-to-end user workflows
|
||||
- **Database Testing**: Factories and seeders
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### **Query Optimization**
|
||||
|
||||
- **Eager Loading**: Prevent N+1 queries
|
||||
- **Query Scoping**: Team-based filtering
|
||||
- **Database Indexing**: Optimized for common queries
|
||||
|
||||
### **Caching Strategy**
|
||||
|
||||
- **Redis**: Session and cache storage
|
||||
- **Model Caching**: Frequently accessed data
|
||||
- **Query Caching**: Expensive query results
|
||||
|
||||
### **Background Processing**
|
||||
|
||||
- **Queue Workers**: Horizon-managed job processing
|
||||
- **Job Batching**: Related job grouping
|
||||
- **Failed Job Handling**: Automatic retry logic
|
53
.cursor/rules/cursor_rules.mdc
Normal file
53
.cursor/rules/cursor_rules.mdc
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
description: Guidelines for creating and maintaining Cursor rules to ensure consistency and effectiveness.
|
||||
globs: .cursor/rules/*.mdc
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
- **Required Rule Structure:**
|
||||
```markdown
|
||||
---
|
||||
description: Clear, one-line description of what the rule enforces
|
||||
globs: path/to/files/*.ext, other/path/**/*
|
||||
alwaysApply: boolean
|
||||
---
|
||||
|
||||
- **Main Points in Bold**
|
||||
- Sub-points with details
|
||||
- Examples and explanations
|
||||
```
|
||||
|
||||
- **File References:**
|
||||
- Use `[filename](mdc:path/to/file)` ([filename](mdc:filename)) to reference files
|
||||
- Example: [prisma.mdc](mdc:.cursor/rules/prisma.mdc) for rule references
|
||||
- Example: [schema.prisma](mdc:prisma/schema.prisma) for code references
|
||||
|
||||
- **Code Examples:**
|
||||
- Use language-specific code blocks
|
||||
```typescript
|
||||
// ✅ DO: Show good examples
|
||||
const goodExample = true;
|
||||
|
||||
// ❌ DON'T: Show anti-patterns
|
||||
const badExample = false;
|
||||
```
|
||||
|
||||
- **Rule Content Guidelines:**
|
||||
- Start with high-level overview
|
||||
- Include specific, actionable requirements
|
||||
- Show examples of correct implementation
|
||||
- Reference existing code when possible
|
||||
- Keep rules DRY by referencing other rules
|
||||
|
||||
- **Rule Maintenance:**
|
||||
- Update rules when new patterns emerge
|
||||
- Add examples from actual codebase
|
||||
- Remove outdated patterns
|
||||
- Cross-reference related rules
|
||||
|
||||
- **Best Practices:**
|
||||
- Use bullet points for clarity
|
||||
- Keep descriptions concise
|
||||
- Include both DO and DON'T examples
|
||||
- Reference actual code over theoretical examples
|
||||
- Use consistent formatting across rules
|
306
.cursor/rules/database-patterns.mdc
Normal file
306
.cursor/rules/database-patterns.mdc
Normal file
@@ -0,0 +1,306 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Coolify Database Architecture & Patterns
|
||||
|
||||
## Database Strategy
|
||||
|
||||
Coolify uses **PostgreSQL 15** as the primary database with **Redis 7** for caching and real-time features. The architecture supports managing multiple external databases across different servers.
|
||||
|
||||
## Primary Database (PostgreSQL)
|
||||
|
||||
### Core Tables & Models
|
||||
|
||||
#### User & Team Management
|
||||
- **[User.php](mdc:app/Models/User.php)** - User authentication and profiles
|
||||
- **[Team.php](mdc:app/Models/Team.php)** - Multi-tenant organization structure
|
||||
- **[TeamInvitation.php](mdc:app/Models/TeamInvitation.php)** - Team collaboration invitations
|
||||
- **[PersonalAccessToken.php](mdc:app/Models/PersonalAccessToken.php)** - API token management
|
||||
|
||||
#### Infrastructure Management
|
||||
- **[Server.php](mdc:app/Models/Server.php)** - Physical/virtual server definitions (46KB, complex)
|
||||
- **[PrivateKey.php](mdc:app/Models/PrivateKey.php)** - SSH key management
|
||||
- **[ServerSetting.php](mdc:app/Models/ServerSetting.php)** - Server-specific configurations
|
||||
|
||||
#### Project Organization
|
||||
- **[Project.php](mdc:app/Models/Project.php)** - Project containers for applications
|
||||
- **[Environment.php](mdc:app/Models/Environment.php)** - Environment isolation (staging, production, etc.)
|
||||
- **[ProjectSetting.php](mdc:app/Models/ProjectSetting.php)** - Project-specific settings
|
||||
|
||||
#### Application Deployment
|
||||
- **[Application.php](mdc:app/Models/Application.php)** - Main application entity (74KB, highly complex)
|
||||
- **[ApplicationSetting.php](mdc:app/Models/ApplicationSetting.php)** - Application configurations
|
||||
- **[ApplicationDeploymentQueue.php](mdc:app/Models/ApplicationDeploymentQueue.php)** - Deployment orchestration
|
||||
- **[ApplicationPreview.php](mdc:app/Models/ApplicationPreview.php)** - Preview environment management
|
||||
|
||||
#### Service Management
|
||||
- **[Service.php](mdc:app/Models/Service.php)** - Service definitions (58KB, complex)
|
||||
- **[ServiceApplication.php](mdc:app/Models/ServiceApplication.php)** - Service components
|
||||
- **[ServiceDatabase.php](mdc:app/Models/ServiceDatabase.php)** - Service-attached databases
|
||||
|
||||
## Database Type Support
|
||||
|
||||
### Standalone Database Models
|
||||
Each database type has its own dedicated model with specific configurations:
|
||||
|
||||
#### SQL Databases
|
||||
- **[StandalonePostgresql.php](mdc:app/Models/StandalonePostgresql.php)** - PostgreSQL instances
|
||||
- **[StandaloneMysql.php](mdc:app/Models/StandaloneMysql.php)** - MySQL instances
|
||||
- **[StandaloneMariadb.php](mdc:app/Models/StandaloneMariadb.php)** - MariaDB instances
|
||||
|
||||
#### NoSQL & Analytics
|
||||
- **[StandaloneMongodb.php](mdc:app/Models/StandaloneMongodb.php)** - MongoDB instances
|
||||
- **[StandaloneClickhouse.php](mdc:app/Models/StandaloneClickhouse.php)** - ClickHouse analytics
|
||||
|
||||
#### Caching & In-Memory
|
||||
- **[StandaloneRedis.php](mdc:app/Models/StandaloneRedis.php)** - Redis instances
|
||||
- **[StandaloneKeydb.php](mdc:app/Models/StandaloneKeydb.php)** - KeyDB instances
|
||||
- **[StandaloneDragonfly.php](mdc:app/Models/StandaloneDragonfly.php)** - Dragonfly instances
|
||||
|
||||
## Configuration Management
|
||||
|
||||
### Environment Variables
|
||||
- **[EnvironmentVariable.php](mdc:app/Models/EnvironmentVariable.php)** - Application-specific environment variables
|
||||
- **[SharedEnvironmentVariable.php](mdc:app/Models/SharedEnvironmentVariable.php)** - Shared across applications
|
||||
|
||||
### Settings Hierarchy
|
||||
- **[InstanceSettings.php](mdc:app/Models/InstanceSettings.php)** - Global Coolify instance settings
|
||||
- **[ServerSetting.php](mdc:app/Models/ServerSetting.php)** - Server-specific settings
|
||||
- **[ProjectSetting.php](mdc:app/Models/ProjectSetting.php)** - Project-level settings
|
||||
- **[ApplicationSetting.php](mdc:app/Models/ApplicationSetting.php)** - Application settings
|
||||
|
||||
## Storage & Backup Systems
|
||||
|
||||
### Storage Management
|
||||
- **[S3Storage.php](mdc:app/Models/S3Storage.php)** - S3-compatible storage configurations
|
||||
- **[LocalFileVolume.php](mdc:app/Models/LocalFileVolume.php)** - Local filesystem volumes
|
||||
- **[LocalPersistentVolume.php](mdc:app/Models/LocalPersistentVolume.php)** - Persistent volume management
|
||||
|
||||
### Backup Infrastructure
|
||||
- **[ScheduledDatabaseBackup.php](mdc:app/Models/ScheduledDatabaseBackup.php)** - Automated backup scheduling
|
||||
- **[ScheduledDatabaseBackupExecution.php](mdc:app/Models/ScheduledDatabaseBackupExecution.php)** - Backup execution tracking
|
||||
|
||||
### Task Scheduling
|
||||
- **[ScheduledTask.php](mdc:app/Models/ScheduledTask.php)** - Cron job management
|
||||
- **[ScheduledTaskExecution.php](mdc:app/Models/ScheduledTaskExecution.php)** - Task execution history
|
||||
|
||||
## Notification & Integration Models
|
||||
|
||||
### Notification Channels
|
||||
- **[EmailNotificationSettings.php](mdc:app/Models/EmailNotificationSettings.php)** - Email notifications
|
||||
- **[DiscordNotificationSettings.php](mdc:app/Models/DiscordNotificationSettings.php)** - Discord integration
|
||||
- **[SlackNotificationSettings.php](mdc:app/Models/SlackNotificationSettings.php)** - Slack integration
|
||||
- **[TelegramNotificationSettings.php](mdc:app/Models/TelegramNotificationSettings.php)** - Telegram bot
|
||||
- **[PushoverNotificationSettings.php](mdc:app/Models/PushoverNotificationSettings.php)** - Pushover notifications
|
||||
|
||||
### Source Control Integration
|
||||
- **[GithubApp.php](mdc:app/Models/GithubApp.php)** - GitHub App integration
|
||||
- **[GitlabApp.php](mdc:app/Models/GitlabApp.php)** - GitLab integration
|
||||
|
||||
### OAuth & Authentication
|
||||
- **[OauthSetting.php](mdc:app/Models/OauthSetting.php)** - OAuth provider configurations
|
||||
|
||||
## Docker & Container Management
|
||||
|
||||
### Container Orchestration
|
||||
- **[StandaloneDocker.php](mdc:app/Models/StandaloneDocker.php)** - Standalone Docker containers
|
||||
- **[SwarmDocker.php](mdc:app/Models/SwarmDocker.php)** - Docker Swarm management
|
||||
|
||||
### SSL & Security
|
||||
- **[SslCertificate.php](mdc:app/Models/SslCertificate.php)** - SSL certificate management
|
||||
|
||||
## Database Migration Strategy
|
||||
|
||||
### Migration Location: [database/migrations/](mdc:database/migrations)
|
||||
|
||||
#### Migration Patterns
|
||||
```php
|
||||
// Typical Coolify migration structure
|
||||
Schema::create('applications', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('fqdn')->nullable();
|
||||
$table->json('environment_variables')->nullable();
|
||||
$table->foreignId('destination_id');
|
||||
$table->foreignId('source_id');
|
||||
$table->timestamps();
|
||||
});
|
||||
```
|
||||
|
||||
### Schema Versioning
|
||||
- **Incremental migrations** for database evolution
|
||||
- **Data migrations** for complex transformations
|
||||
- **Rollback support** for deployment safety
|
||||
|
||||
## Eloquent Model Patterns
|
||||
|
||||
### Base Model Structure
|
||||
- **[BaseModel.php](mdc:app/Models/BaseModel.php)** - Common model functionality
|
||||
- **UUID primary keys** for distributed systems
|
||||
- **Soft deletes** for audit trails
|
||||
- **Activity logging** with Spatie package
|
||||
|
||||
### Relationship Patterns
|
||||
```php
|
||||
// Typical relationship structure in Application model
|
||||
class Application extends Model
|
||||
{
|
||||
public function server()
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
public function environment()
|
||||
{
|
||||
return $this->belongsTo(Environment::class);
|
||||
}
|
||||
|
||||
public function deployments()
|
||||
{
|
||||
return $this->hasMany(ApplicationDeploymentQueue::class);
|
||||
}
|
||||
|
||||
public function environmentVariables()
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Model Traits
|
||||
```php
|
||||
// Common traits used across models
|
||||
use SoftDeletes;
|
||||
use LogsActivity;
|
||||
use HasFactory;
|
||||
use HasUuids;
|
||||
```
|
||||
|
||||
## Caching Strategy (Redis)
|
||||
|
||||
### Cache Usage Patterns
|
||||
- **Session storage** - User authentication sessions
|
||||
- **Queue backend** - Background job processing
|
||||
- **Model caching** - Expensive query results
|
||||
- **Real-time data** - WebSocket state management
|
||||
|
||||
### Cache Keys Structure
|
||||
```
|
||||
coolify:session:{session_id}
|
||||
coolify:server:{server_id}:status
|
||||
coolify:deployment:{deployment_id}:logs
|
||||
coolify:user:{user_id}:teams
|
||||
```
|
||||
|
||||
## Query Optimization Patterns
|
||||
|
||||
### Eager Loading
|
||||
```php
|
||||
// Optimized queries with relationships
|
||||
$applications = Application::with([
|
||||
'server',
|
||||
'environment.project',
|
||||
'environmentVariables',
|
||||
'deployments' => function ($query) {
|
||||
$query->latest()->limit(5);
|
||||
}
|
||||
])->get();
|
||||
```
|
||||
|
||||
### Chunking for Large Datasets
|
||||
```php
|
||||
// Processing large datasets efficiently
|
||||
Server::chunk(100, function ($servers) {
|
||||
foreach ($servers as $server) {
|
||||
// Process server monitoring
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Database Indexes
|
||||
- **Primary keys** on all tables
|
||||
- **Foreign key indexes** for relationships
|
||||
- **Composite indexes** for common queries
|
||||
- **Unique constraints** for business rules
|
||||
|
||||
## Data Consistency Patterns
|
||||
|
||||
### Database Transactions
|
||||
```php
|
||||
// Atomic operations for deployment
|
||||
DB::transaction(function () {
|
||||
$application = Application::create($data);
|
||||
$application->environmentVariables()->createMany($envVars);
|
||||
$application->deployments()->create(['status' => 'queued']);
|
||||
});
|
||||
```
|
||||
|
||||
### Model Events
|
||||
```php
|
||||
// Automatic cleanup on model deletion
|
||||
class Application extends Model
|
||||
{
|
||||
protected static function booted()
|
||||
{
|
||||
static::deleting(function ($application) {
|
||||
$application->environmentVariables()->delete();
|
||||
$application->deployments()->delete();
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Backup & Recovery
|
||||
|
||||
### Database Backup Strategy
|
||||
- **Automated PostgreSQL backups** via scheduled tasks
|
||||
- **Point-in-time recovery** capability
|
||||
- **Cross-region backup** replication
|
||||
- **Backup verification** and testing
|
||||
|
||||
### Data Export/Import
|
||||
- **Application configurations** export/import
|
||||
- **Environment variable** bulk operations
|
||||
- **Server configurations** backup and restore
|
||||
|
||||
## Performance Monitoring
|
||||
|
||||
### Query Performance
|
||||
- **Laravel Telescope** for development debugging
|
||||
- **Slow query logging** in production
|
||||
- **Database connection** pooling
|
||||
- **Read replica** support for scaling
|
||||
|
||||
### Metrics Collection
|
||||
- **Database size** monitoring
|
||||
- **Connection count** tracking
|
||||
- **Query execution time** analysis
|
||||
- **Cache hit rates** monitoring
|
||||
|
||||
## Multi-Tenancy Pattern
|
||||
|
||||
### Team-Based Isolation
|
||||
```php
|
||||
// Global scope for team-based filtering
|
||||
class Application extends Model
|
||||
{
|
||||
protected static function booted()
|
||||
{
|
||||
static::addGlobalScope('team', function (Builder $builder) {
|
||||
if (auth()->user()) {
|
||||
$builder->whereHas('environment.project', function ($query) {
|
||||
$query->where('team_id', auth()->user()->currentTeam->id);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Data Separation
|
||||
- **Team-scoped queries** by default
|
||||
- **Cross-team access** controls
|
||||
- **Admin access** patterns
|
||||
- **Data isolation** guarantees
|
310
.cursor/rules/deployment-architecture.mdc
Normal file
310
.cursor/rules/deployment-architecture.mdc
Normal file
@@ -0,0 +1,310 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Coolify Deployment Architecture
|
||||
|
||||
## Deployment Philosophy
|
||||
|
||||
Coolify orchestrates **Docker-based deployments** across multiple servers with automated configuration generation, zero-downtime deployments, and comprehensive monitoring.
|
||||
|
||||
## Core Deployment Components
|
||||
|
||||
### Deployment Models
|
||||
- **[Application.php](mdc:app/Models/Application.php)** - Main application entity with deployment configurations
|
||||
- **[ApplicationDeploymentQueue.php](mdc:app/Models/ApplicationDeploymentQueue.php)** - Deployment job orchestration
|
||||
- **[Service.php](mdc:app/Models/Service.php)** - Multi-container service definitions
|
||||
- **[Server.php](mdc:app/Models/Server.php)** - Target deployment infrastructure
|
||||
|
||||
### Infrastructure Management
|
||||
- **[PrivateKey.php](mdc:app/Models/PrivateKey.php)** - SSH key management for secure server access
|
||||
- **[StandaloneDocker.php](mdc:app/Models/StandaloneDocker.php)** - Single container deployments
|
||||
- **[SwarmDocker.php](mdc:app/Models/SwarmDocker.php)** - Docker Swarm orchestration
|
||||
|
||||
## Deployment Workflow
|
||||
|
||||
### 1. Source Code Integration
|
||||
```
|
||||
Git Repository → Webhook → Coolify → Build & Deploy
|
||||
```
|
||||
|
||||
#### Source Control Models
|
||||
- **[GithubApp.php](mdc:app/Models/GithubApp.php)** - GitHub integration and webhooks
|
||||
- **[GitlabApp.php](mdc:app/Models/GitlabApp.php)** - GitLab CI/CD integration
|
||||
|
||||
#### Deployment Triggers
|
||||
- **Git push** to configured branches
|
||||
- **Manual deployment** via UI
|
||||
- **Scheduled deployments** via cron
|
||||
- **API-triggered** deployments
|
||||
|
||||
### 2. Build Process
|
||||
```
|
||||
Source Code → Docker Build → Image Registry → Deployment
|
||||
```
|
||||
|
||||
#### Build Configurations
|
||||
- **Dockerfile detection** and custom Dockerfile support
|
||||
- **Buildpack integration** for framework detection
|
||||
- **Multi-stage builds** for optimization
|
||||
- **Cache layer** management for faster builds
|
||||
|
||||
### 3. Deployment Orchestration
|
||||
```
|
||||
Queue Job → Configuration Generation → Container Deployment → Health Checks
|
||||
```
|
||||
|
||||
## Deployment Actions
|
||||
|
||||
### Location: [app/Actions/](mdc:app/Actions)
|
||||
|
||||
#### Application Deployment Actions
|
||||
- **Application/** - Core application deployment logic
|
||||
- **Docker/** - Docker container management
|
||||
- **Service/** - Multi-container service orchestration
|
||||
- **Proxy/** - Reverse proxy configuration
|
||||
|
||||
#### Database Actions
|
||||
- **Database/** - Database deployment and management
|
||||
- Automated backup scheduling
|
||||
- Connection management and health checks
|
||||
|
||||
#### Server Management Actions
|
||||
- **Server/** - Server provisioning and configuration
|
||||
- SSH connection establishment
|
||||
- Docker daemon management
|
||||
|
||||
## Configuration Generation
|
||||
|
||||
### Dynamic Configuration
|
||||
- **[ConfigurationGenerator.php](mdc:app/Services/ConfigurationGenerator.php)** - Generates deployment configurations
|
||||
- **[ConfigurationRepository.php](mdc:app/Services/ConfigurationRepository.php)** - Configuration management
|
||||
|
||||
### Generated Configurations
|
||||
#### Docker Compose Files
|
||||
```yaml
|
||||
# Generated docker-compose.yml structure
|
||||
version: '3.8'
|
||||
services:
|
||||
app:
|
||||
image: ${APP_IMAGE}
|
||||
environment:
|
||||
- ${ENV_VARIABLES}
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.app.rule=Host(`${FQDN}`)
|
||||
volumes:
|
||||
- ${VOLUME_MAPPINGS}
|
||||
networks:
|
||||
- coolify
|
||||
```
|
||||
|
||||
#### Nginx Configurations
|
||||
- **Reverse proxy** setup
|
||||
- **SSL termination** with automatic certificates
|
||||
- **Load balancing** for multiple instances
|
||||
- **Custom headers** and routing rules
|
||||
|
||||
## Container Orchestration
|
||||
|
||||
### Docker Integration
|
||||
- **[DockerImageParser.php](mdc:app/Services/DockerImageParser.php)** - Parse and validate Docker images
|
||||
- **Container lifecycle** management
|
||||
- **Resource allocation** and limits
|
||||
- **Network isolation** and communication
|
||||
|
||||
### Volume Management
|
||||
- **[LocalFileVolume.php](mdc:app/Models/LocalFileVolume.php)** - Persistent file storage
|
||||
- **[LocalPersistentVolume.php](mdc:app/Models/LocalPersistentVolume.php)** - Data persistence
|
||||
- **Backup integration** for volume data
|
||||
|
||||
### Network Configuration
|
||||
- **Custom Docker networks** for isolation
|
||||
- **Service discovery** between containers
|
||||
- **Port mapping** and exposure
|
||||
- **SSL/TLS termination**
|
||||
|
||||
## Environment Management
|
||||
|
||||
### Environment Isolation
|
||||
- **[Environment.php](mdc:app/Models/Environment.php)** - Development, staging, production environments
|
||||
- **[EnvironmentVariable.php](mdc:app/Models/EnvironmentVariable.php)** - Application-specific variables
|
||||
- **[SharedEnvironmentVariable.php](mdc:app/Models/SharedEnvironmentVariable.php)** - Cross-application variables
|
||||
|
||||
### Configuration Hierarchy
|
||||
```
|
||||
Instance Settings → Server Settings → Project Settings → Application Settings
|
||||
```
|
||||
|
||||
## Preview Environments
|
||||
|
||||
### Git-Based Previews
|
||||
- **[ApplicationPreview.php](mdc:app/Models/ApplicationPreview.php)** - Preview environment management
|
||||
- **Automatic PR/MR previews** for feature branches
|
||||
- **Isolated environments** for testing
|
||||
- **Automatic cleanup** after merge/close
|
||||
|
||||
### Preview Workflow
|
||||
```
|
||||
Feature Branch → Auto-Deploy → Preview URL → Review → Cleanup
|
||||
```
|
||||
|
||||
## SSL & Security
|
||||
|
||||
### Certificate Management
|
||||
- **[SslCertificate.php](mdc:app/Models/SslCertificate.php)** - SSL certificate automation
|
||||
- **Let's Encrypt** integration for free certificates
|
||||
- **Custom certificate** upload support
|
||||
- **Automatic renewal** and monitoring
|
||||
|
||||
### Security Patterns
|
||||
- **Private Docker networks** for container isolation
|
||||
- **SSH key-based** server authentication
|
||||
- **Environment variable** encryption
|
||||
- **Access control** via team permissions
|
||||
|
||||
## Backup & Recovery
|
||||
|
||||
### Database Backups
|
||||
- **[ScheduledDatabaseBackup.php](mdc:app/Models/ScheduledDatabaseBackup.php)** - Automated database backups
|
||||
- **[ScheduledDatabaseBackupExecution.php](mdc:app/Models/ScheduledDatabaseBackupExecution.php)** - Backup execution tracking
|
||||
- **S3-compatible storage** for backup destinations
|
||||
|
||||
### Application Backups
|
||||
- **Volume snapshots** for persistent data
|
||||
- **Configuration export** for disaster recovery
|
||||
- **Cross-region replication** for high availability
|
||||
|
||||
## Monitoring & Logging
|
||||
|
||||
### Real-Time Monitoring
|
||||
- **[ActivityMonitor.php](mdc:app/Livewire/ActivityMonitor.php)** - Live deployment monitoring
|
||||
- **WebSocket-based** log streaming
|
||||
- **Container health checks** and alerts
|
||||
- **Resource usage** tracking
|
||||
|
||||
### Deployment Logs
|
||||
- **Build process** logging
|
||||
- **Container startup** logs
|
||||
- **Application runtime** logs
|
||||
- **Error tracking** and alerting
|
||||
|
||||
## Queue System
|
||||
|
||||
### Background Jobs
|
||||
Location: [app/Jobs/](mdc:app/Jobs)
|
||||
- **Deployment jobs** for async processing
|
||||
- **Server monitoring** jobs
|
||||
- **Backup scheduling** jobs
|
||||
- **Notification delivery** jobs
|
||||
|
||||
### Queue Processing
|
||||
- **Redis-backed** job queues
|
||||
- **Laravel Horizon** for queue monitoring
|
||||
- **Failed job** retry mechanisms
|
||||
- **Queue worker** auto-scaling
|
||||
|
||||
## Multi-Server Deployment
|
||||
|
||||
### Server Types
|
||||
- **Standalone servers** - Single Docker host
|
||||
- **Docker Swarm** - Multi-node orchestration
|
||||
- **Remote servers** - SSH-based deployment
|
||||
- **Local development** - Docker Desktop integration
|
||||
|
||||
### Load Balancing
|
||||
- **Traefik integration** for automatic load balancing
|
||||
- **Health check** based routing
|
||||
- **Blue-green deployments** for zero downtime
|
||||
- **Rolling updates** with configurable strategies
|
||||
|
||||
## Deployment Strategies
|
||||
|
||||
### Zero-Downtime Deployment
|
||||
```
|
||||
Old Container → New Container Build → Health Check → Traffic Switch → Old Container Cleanup
|
||||
```
|
||||
|
||||
### Blue-Green Deployment
|
||||
- **Parallel environments** for safe deployments
|
||||
- **Instant rollback** capability
|
||||
- **Database migration** handling
|
||||
- **Configuration synchronization**
|
||||
|
||||
### Rolling Updates
|
||||
- **Gradual instance** replacement
|
||||
- **Configurable update** strategy
|
||||
- **Automatic rollback** on failure
|
||||
- **Health check** validation
|
||||
|
||||
## API Integration
|
||||
|
||||
### Deployment API
|
||||
Routes: [routes/api.php](mdc:routes/api.php)
|
||||
- **RESTful endpoints** for deployment management
|
||||
- **Webhook receivers** for CI/CD integration
|
||||
- **Status reporting** endpoints
|
||||
- **Deployment triggering** via API
|
||||
|
||||
### Authentication
|
||||
- **Laravel Sanctum** API tokens
|
||||
- **Team-based** access control
|
||||
- **Rate limiting** for API calls
|
||||
- **Audit logging** for API usage
|
||||
|
||||
## Error Handling & Recovery
|
||||
|
||||
### Deployment Failure Recovery
|
||||
- **Automatic rollback** on deployment failure
|
||||
- **Health check** failure handling
|
||||
- **Container crash** recovery
|
||||
- **Resource exhaustion** protection
|
||||
|
||||
### Monitoring & Alerting
|
||||
- **Failed deployment** notifications
|
||||
- **Resource threshold** alerts
|
||||
- **SSL certificate** expiry warnings
|
||||
- **Backup failure** notifications
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Build Optimization
|
||||
- **Docker layer** caching
|
||||
- **Multi-stage builds** for smaller images
|
||||
- **Build artifact** reuse
|
||||
- **Parallel build** processing
|
||||
|
||||
### Runtime Optimization
|
||||
- **Container resource** limits
|
||||
- **Auto-scaling** based on metrics
|
||||
- **Connection pooling** for databases
|
||||
- **CDN integration** for static assets
|
||||
|
||||
## Compliance & Governance
|
||||
|
||||
### Audit Trail
|
||||
- **Deployment history** tracking
|
||||
- **Configuration changes** logging
|
||||
- **User action** auditing
|
||||
- **Resource access** monitoring
|
||||
|
||||
### Backup Compliance
|
||||
- **Retention policies** for backups
|
||||
- **Encryption at rest** for sensitive data
|
||||
- **Cross-region** backup replication
|
||||
- **Recovery testing** automation
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
### CI/CD Integration
|
||||
- **GitHub Actions** compatibility
|
||||
- **GitLab CI** pipeline integration
|
||||
- **Custom webhook** endpoints
|
||||
- **Build status** reporting
|
||||
|
||||
### External Services
|
||||
- **S3-compatible** storage integration
|
||||
- **External database** connections
|
||||
- **Third-party monitoring** tools
|
||||
- **Custom notification** channels
|
219
.cursor/rules/dev_workflow.mdc
Normal file
219
.cursor/rules/dev_workflow.mdc
Normal file
@@ -0,0 +1,219 @@
|
||||
---
|
||||
description: Guide for using Task Master to manage task-driven development workflows
|
||||
globs: **/*
|
||||
alwaysApply: true
|
||||
---
|
||||
# Task Master Development Workflow
|
||||
|
||||
This guide outlines the typical process for using Task Master to manage software development projects.
|
||||
|
||||
## Primary Interaction: MCP Server vs. CLI
|
||||
|
||||
Task Master offers two primary ways to interact:
|
||||
|
||||
1. **MCP Server (Recommended for Integrated Tools)**:
|
||||
- For AI agents and integrated development environments (like Cursor), interacting via the **MCP server is the preferred method**.
|
||||
- The MCP server exposes Task Master functionality through a set of tools (e.g., `get_tasks`, `add_subtask`).
|
||||
- This method offers better performance, structured data exchange, and richer error handling compared to CLI parsing.
|
||||
- Refer to [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) for details on the MCP architecture and available tools.
|
||||
- A comprehensive list and description of MCP tools and their corresponding CLI commands can be found in [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc).
|
||||
- **Restart the MCP server** if core logic in `scripts/modules` or MCP tool/direct function definitions change.
|
||||
|
||||
2. **`task-master` CLI (For Users & Fallback)**:
|
||||
- The global `task-master` command provides a user-friendly interface for direct terminal interaction.
|
||||
- It can also serve as a fallback if the MCP server is inaccessible or a specific function isn't exposed via MCP.
|
||||
- Install globally with `npm install -g task-master-ai` or use locally via `npx task-master-ai ...`.
|
||||
- The CLI commands often mirror the MCP tools (e.g., `task-master list` corresponds to `get_tasks`).
|
||||
- Refer to [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc) for a detailed command reference.
|
||||
|
||||
## Standard Development Workflow Process
|
||||
|
||||
- Start new projects by running `initialize_project` tool / `task-master init` or `parse_prd` / `task-master parse-prd --input='<prd-file.txt>'` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to generate initial tasks.json
|
||||
- Begin coding sessions with `get_tasks` / `task-master list` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to see current tasks, status, and IDs
|
||||
- Determine the next task to work on using `next_task` / `task-master next` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Analyze task complexity with `analyze_project_complexity` / `task-master analyze-complexity --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) before breaking down tasks
|
||||
- Review complexity report using `complexity_report` / `task-master complexity-report` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Select tasks based on dependencies (all marked 'done'), priority level, and ID order
|
||||
- Clarify tasks by checking task files in tasks/ directory or asking for user input
|
||||
- View specific task details using `get_task` / `task-master show <id>` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to understand implementation requirements
|
||||
- Break down complex tasks using `expand_task` / `task-master expand --id=<id> --force --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) with appropriate flags like `--force` (to replace existing subtasks) and `--research`.
|
||||
- Clear existing subtasks if needed using `clear_subtasks` / `task-master clear-subtasks --id=<id>` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) before regenerating
|
||||
- Implement code following task details, dependencies, and project standards
|
||||
- Verify tasks according to test strategies before marking as complete (See [`tests.mdc`](mdc:.cursor/rules/tests.mdc))
|
||||
- Mark completed tasks with `set_task_status` / `task-master set-status --id=<id> --status=done` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc))
|
||||
- Update dependent tasks when implementation differs from original plan using `update` / `task-master update --from=<id> --prompt="..."` or `update_task` / `task-master update-task --id=<id> --prompt="..."` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc))
|
||||
- Add new tasks discovered during implementation using `add_task` / `task-master add-task --prompt="..." --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Add new subtasks as needed using `add_subtask` / `task-master add-subtask --parent=<id> --title="..."` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Append notes or details to subtasks using `update_subtask` / `task-master update-subtask --id=<subtaskId> --prompt='Add implementation notes here...\nMore details...'` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Generate task files with `generate` / `task-master generate` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) after updating tasks.json
|
||||
- Maintain valid dependency structure with `add_dependency`/`remove_dependency` tools or `task-master add-dependency`/`remove-dependency` commands, `validate_dependencies` / `task-master validate-dependencies`, and `fix_dependencies` / `task-master fix-dependencies` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) when needed
|
||||
- Respect dependency chains and task priorities when selecting work
|
||||
- Report progress regularly using `get_tasks` / `task-master list`
|
||||
|
||||
## Task Complexity Analysis
|
||||
|
||||
- Run `analyze_project_complexity` / `task-master analyze-complexity --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) for comprehensive analysis
|
||||
- Review complexity report via `complexity_report` / `task-master complexity-report` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) for a formatted, readable version.
|
||||
- Focus on tasks with highest complexity scores (8-10) for detailed breakdown
|
||||
- Use analysis results to determine appropriate subtask allocation
|
||||
- Note that reports are automatically used by the `expand_task` tool/command
|
||||
|
||||
## Task Breakdown Process
|
||||
|
||||
- Use `expand_task` / `task-master expand --id=<id>`. It automatically uses the complexity report if found, otherwise generates default number of subtasks.
|
||||
- Use `--num=<number>` to specify an explicit number of subtasks, overriding defaults or complexity report recommendations.
|
||||
- Add `--research` flag to leverage Perplexity AI for research-backed expansion.
|
||||
- Add `--force` flag to clear existing subtasks before generating new ones (default is to append).
|
||||
- Use `--prompt="<context>"` to provide additional context when needed.
|
||||
- Review and adjust generated subtasks as necessary.
|
||||
- Use `expand_all` tool or `task-master expand --all` to expand multiple pending tasks at once, respecting flags like `--force` and `--research`.
|
||||
- If subtasks need complete replacement (regardless of the `--force` flag on `expand`), clear them first with `clear_subtasks` / `task-master clear-subtasks --id=<id>`.
|
||||
|
||||
## Implementation Drift Handling
|
||||
|
||||
- When implementation differs significantly from planned approach
|
||||
- When future tasks need modification due to current implementation choices
|
||||
- When new dependencies or requirements emerge
|
||||
- Use `update` / `task-master update --from=<futureTaskId> --prompt='<explanation>\nUpdate context...' --research` to update multiple future tasks.
|
||||
- Use `update_task` / `task-master update-task --id=<taskId> --prompt='<explanation>\nUpdate context...' --research` to update a single specific task.
|
||||
|
||||
## Task Status Management
|
||||
|
||||
- Use 'pending' for tasks ready to be worked on
|
||||
- Use 'done' for completed and verified tasks
|
||||
- Use 'deferred' for postponed tasks
|
||||
- Add custom status values as needed for project-specific workflows
|
||||
|
||||
## Task Structure Fields
|
||||
|
||||
- **id**: Unique identifier for the task (Example: `1`, `1.1`)
|
||||
- **title**: Brief, descriptive title (Example: `"Initialize Repo"`)
|
||||
- **description**: Concise summary of what the task involves (Example: `"Create a new repository, set up initial structure."`)
|
||||
- **status**: Current state of the task (Example: `"pending"`, `"done"`, `"deferred"`)
|
||||
- **dependencies**: IDs of prerequisite tasks (Example: `[1, 2.1]`)
|
||||
- Dependencies are displayed with status indicators (✅ for completed, ⏱️ for pending)
|
||||
- This helps quickly identify which prerequisite tasks are blocking work
|
||||
- **priority**: Importance level (Example: `"high"`, `"medium"`, `"low"`)
|
||||
- **details**: In-depth implementation instructions (Example: `"Use GitHub client ID/secret, handle callback, set session token."`)
|
||||
- **testStrategy**: Verification approach (Example: `"Deploy and call endpoint to confirm 'Hello World' response."`)
|
||||
- **subtasks**: List of smaller, more specific tasks (Example: `[{"id": 1, "title": "Configure OAuth", ...}]`)
|
||||
- Refer to task structure details (previously linked to `tasks.mdc`).
|
||||
|
||||
## Configuration Management (Updated)
|
||||
|
||||
Taskmaster configuration is managed through two main mechanisms:
|
||||
|
||||
1. **`.taskmasterconfig` File (Primary):**
|
||||
* Located in the project root directory.
|
||||
* Stores most configuration settings: AI model selections (main, research, fallback), parameters (max tokens, temperature), logging level, default subtasks/priority, project name, etc.
|
||||
* **Managed via `task-master models --setup` command.** Do not edit manually unless you know what you are doing.
|
||||
* **View/Set specific models via `task-master models` command or `models` MCP tool.**
|
||||
* Created automatically when you run `task-master models --setup` for the first time.
|
||||
|
||||
2. **Environment Variables (`.env` / `mcp.json`):**
|
||||
* Used **only** for sensitive API keys and specific endpoint URLs.
|
||||
* Place API keys (one per provider) in a `.env` file in the project root for CLI usage.
|
||||
* For MCP/Cursor integration, configure these keys in the `env` section of `.cursor/mcp.json`.
|
||||
* Available keys/variables: See `assets/env.example` or the Configuration section in the command reference (previously linked to `taskmaster.mdc`).
|
||||
|
||||
**Important:** Non-API key settings (like model selections, `MAX_TOKENS`, `TASKMASTER_LOG_LEVEL`) are **no longer configured via environment variables**. Use the `task-master models` command (or `--setup` for interactive configuration) or the `models` MCP tool.
|
||||
**If AI commands FAIL in MCP** verify that the API key for the selected provider is present in the `env` section of `.cursor/mcp.json`.
|
||||
**If AI commands FAIL in CLI** verify that the API key for the selected provider is present in the `.env` file in the root of the project.
|
||||
|
||||
## Determining the Next Task
|
||||
|
||||
- Run `next_task` / `task-master next` to show the next task to work on.
|
||||
- The command identifies tasks with all dependencies satisfied
|
||||
- Tasks are prioritized by priority level, dependency count, and ID
|
||||
- The command shows comprehensive task information including:
|
||||
- Basic task details and description
|
||||
- Implementation details
|
||||
- Subtasks (if they exist)
|
||||
- Contextual suggested actions
|
||||
- Recommended before starting any new development work
|
||||
- Respects your project's dependency structure
|
||||
- Ensures tasks are completed in the appropriate sequence
|
||||
- Provides ready-to-use commands for common task actions
|
||||
|
||||
## Viewing Specific Task Details
|
||||
|
||||
- Run `get_task` / `task-master show <id>` to view a specific task.
|
||||
- Use dot notation for subtasks: `task-master show 1.2` (shows subtask 2 of task 1)
|
||||
- Displays comprehensive information similar to the next command, but for a specific task
|
||||
- For parent tasks, shows all subtasks and their current status
|
||||
- For subtasks, shows parent task information and relationship
|
||||
- Provides contextual suggested actions appropriate for the specific task
|
||||
- Useful for examining task details before implementation or checking status
|
||||
|
||||
## Managing Task Dependencies
|
||||
|
||||
- Use `add_dependency` / `task-master add-dependency --id=<id> --depends-on=<id>` to add a dependency.
|
||||
- Use `remove_dependency` / `task-master remove-dependency --id=<id> --depends-on=<id>` to remove a dependency.
|
||||
- The system prevents circular dependencies and duplicate dependency entries
|
||||
- Dependencies are checked for existence before being added or removed
|
||||
- Task files are automatically regenerated after dependency changes
|
||||
- Dependencies are visualized with status indicators in task listings and files
|
||||
|
||||
## Iterative Subtask Implementation
|
||||
|
||||
Once a task has been broken down into subtasks using `expand_task` or similar methods, follow this iterative process for implementation:
|
||||
|
||||
1. **Understand the Goal (Preparation):**
|
||||
* Use `get_task` / `task-master show <subtaskId>` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to thoroughly understand the specific goals and requirements of the subtask.
|
||||
|
||||
2. **Initial Exploration & Planning (Iteration 1):**
|
||||
* This is the first attempt at creating a concrete implementation plan.
|
||||
* Explore the codebase to identify the precise files, functions, and even specific lines of code that will need modification.
|
||||
* Determine the intended code changes (diffs) and their locations.
|
||||
* Gather *all* relevant details from this exploration phase.
|
||||
|
||||
3. **Log the Plan:**
|
||||
* Run `update_subtask` / `task-master update-subtask --id=<subtaskId> --prompt='<detailed plan>'`.
|
||||
* Provide the *complete and detailed* findings from the exploration phase in the prompt. Include file paths, line numbers, proposed diffs, reasoning, and any potential challenges identified. Do not omit details. The goal is to create a rich, timestamped log within the subtask's `details`.
|
||||
|
||||
4. **Verify the Plan:**
|
||||
* Run `get_task` / `task-master show <subtaskId>` again to confirm that the detailed implementation plan has been successfully appended to the subtask's details.
|
||||
|
||||
5. **Begin Implementation:**
|
||||
* Set the subtask status using `set_task_status` / `task-master set-status --id=<subtaskId> --status=in-progress`.
|
||||
* Start coding based on the logged plan.
|
||||
|
||||
6. **Refine and Log Progress (Iteration 2+):**
|
||||
* As implementation progresses, you will encounter challenges, discover nuances, or confirm successful approaches.
|
||||
* **Before appending new information**: Briefly review the *existing* details logged in the subtask (using `get_task` or recalling from context) to ensure the update adds fresh insights and avoids redundancy.
|
||||
* **Regularly** use `update_subtask` / `task-master update-subtask --id=<subtaskId> --prompt='<update details>\n- What worked...\n- What didn't work...'` to append new findings.
|
||||
* **Crucially, log:**
|
||||
* What worked ("fundamental truths" discovered).
|
||||
* What didn't work and why (to avoid repeating mistakes).
|
||||
* Specific code snippets or configurations that were successful.
|
||||
* Decisions made, especially if confirmed with user input.
|
||||
* Any deviations from the initial plan and the reasoning.
|
||||
* The objective is to continuously enrich the subtask's details, creating a log of the implementation journey that helps the AI (and human developers) learn, adapt, and avoid repeating errors.
|
||||
|
||||
7. **Review & Update Rules (Post-Implementation):**
|
||||
* Once the implementation for the subtask is functionally complete, review all code changes and the relevant chat history.
|
||||
* Identify any new or modified code patterns, conventions, or best practices established during the implementation.
|
||||
* Create new or update existing rules following internal guidelines (previously linked to `cursor_rules.mdc` and `self_improve.mdc`).
|
||||
|
||||
8. **Mark Task Complete:**
|
||||
* After verifying the implementation and updating any necessary rules, mark the subtask as completed: `set_task_status` / `task-master set-status --id=<subtaskId> --status=done`.
|
||||
|
||||
9. **Commit Changes (If using Git):**
|
||||
* Stage the relevant code changes and any updated/new rule files (`git add .`).
|
||||
* Craft a comprehensive Git commit message summarizing the work done for the subtask, including both code implementation and any rule adjustments.
|
||||
* Execute the commit command directly in the terminal (e.g., `git commit -m 'feat(module): Implement feature X for subtask <subtaskId>\n\n- Details about changes...\n- Updated rule Y for pattern Z'`).
|
||||
* Consider if a Changeset is needed according to internal versioning guidelines (previously linked to `changeset.mdc`). If so, run `npm run changeset`, stage the generated file, and amend the commit or create a new one.
|
||||
|
||||
10. **Proceed to Next Subtask:**
|
||||
* Identify the next subtask (e.g., using `next_task` / `task-master next`).
|
||||
|
||||
## Code Analysis & Refactoring Techniques
|
||||
|
||||
- **Top-Level Function Search**:
|
||||
- Useful for understanding module structure or planning refactors.
|
||||
- Use grep/ripgrep to find exported functions/constants:
|
||||
`rg "export (async function|function|const) \w+"` or similar patterns.
|
||||
- Can help compare functions between files during migrations or identify potential naming conflicts.
|
||||
|
||||
---
|
||||
*This workflow provides a general guideline. Adapt it based on your specific project needs and team practices.*
|
653
.cursor/rules/development-workflow.mdc
Normal file
653
.cursor/rules/development-workflow.mdc
Normal file
@@ -0,0 +1,653 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Coolify Development Workflow
|
||||
|
||||
## Development Environment Setup
|
||||
|
||||
### Prerequisites
|
||||
- **PHP 8.4+** - Latest PHP version for modern features
|
||||
- **Node.js 18+** - For frontend asset compilation
|
||||
- **Docker & Docker Compose** - Container orchestration
|
||||
- **PostgreSQL 15** - Primary database
|
||||
- **Redis 7** - Caching and queues
|
||||
|
||||
### Local Development Setup
|
||||
|
||||
#### Using Docker (Recommended)
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/coollabsio/coolify.git
|
||||
cd coolify
|
||||
|
||||
# Copy environment configuration
|
||||
cp .env.example .env
|
||||
|
||||
# Start development environment
|
||||
docker-compose -f docker-compose.dev.yml up -d
|
||||
|
||||
# Install PHP dependencies
|
||||
docker-compose exec app composer install
|
||||
|
||||
# Install Node.js dependencies
|
||||
docker-compose exec app npm install
|
||||
|
||||
# Generate application key
|
||||
docker-compose exec app php artisan key:generate
|
||||
|
||||
# Run database migrations
|
||||
docker-compose exec app php artisan migrate
|
||||
|
||||
# Seed development data
|
||||
docker-compose exec app php artisan db:seed
|
||||
```
|
||||
|
||||
#### Native Development
|
||||
```bash
|
||||
# Install PHP dependencies
|
||||
composer install
|
||||
|
||||
# Install Node.js dependencies
|
||||
npm install
|
||||
|
||||
# Setup environment
|
||||
cp .env.example .env
|
||||
php artisan key:generate
|
||||
|
||||
# Setup database
|
||||
createdb coolify_dev
|
||||
php artisan migrate
|
||||
php artisan db:seed
|
||||
|
||||
# Start development servers
|
||||
php artisan serve &
|
||||
npm run dev &
|
||||
php artisan queue:work &
|
||||
```
|
||||
|
||||
## Development Tools & Configuration
|
||||
|
||||
### Code Quality Tools
|
||||
- **[Laravel Pint](mdc:pint.json)** - PHP code style fixer
|
||||
- **[Rector](mdc:rector.php)** - PHP automated refactoring (989B, 35 lines)
|
||||
- **PHPStan** - Static analysis for type safety
|
||||
- **ESLint** - JavaScript code quality
|
||||
|
||||
### Development Configuration Files
|
||||
- **[docker-compose.dev.yml](mdc:docker-compose.dev.yml)** - Development Docker setup (3.4KB, 126 lines)
|
||||
- **[vite.config.js](mdc:vite.config.js)** - Frontend build configuration (1.0KB, 42 lines)
|
||||
- **[.editorconfig](mdc:.editorconfig)** - Code formatting standards (258B, 19 lines)
|
||||
|
||||
### Git Configuration
|
||||
- **[.gitignore](mdc:.gitignore)** - Version control exclusions (522B, 40 lines)
|
||||
- **[.gitattributes](mdc:.gitattributes)** - Git file handling (185B, 11 lines)
|
||||
|
||||
## Development Workflow Process
|
||||
|
||||
### 1. Feature Development
|
||||
```bash
|
||||
# Create feature branch
|
||||
git checkout -b feature/new-deployment-strategy
|
||||
|
||||
# Make changes following coding standards
|
||||
# Run code quality checks
|
||||
./vendor/bin/pint
|
||||
./vendor/bin/rector process --dry-run
|
||||
./vendor/bin/phpstan analyse
|
||||
|
||||
# Run tests
|
||||
./vendor/bin/pest
|
||||
./vendor/bin/pest --coverage
|
||||
|
||||
# Commit changes
|
||||
git add .
|
||||
git commit -m "feat: implement blue-green deployment strategy"
|
||||
```
|
||||
|
||||
### 2. Code Review Process
|
||||
```bash
|
||||
# Push feature branch
|
||||
git push origin feature/new-deployment-strategy
|
||||
|
||||
# Create pull request with:
|
||||
# - Clear description of changes
|
||||
# - Screenshots for UI changes
|
||||
# - Test coverage information
|
||||
# - Breaking change documentation
|
||||
```
|
||||
|
||||
### 3. Testing Requirements
|
||||
- **Unit tests** for new models and services
|
||||
- **Feature tests** for API endpoints
|
||||
- **Browser tests** for UI changes
|
||||
- **Integration tests** for deployment workflows
|
||||
|
||||
## Coding Standards & Conventions
|
||||
|
||||
### PHP Coding Standards
|
||||
```php
|
||||
// Follow PSR-12 coding standards
|
||||
class ApplicationDeploymentService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DockerService $dockerService,
|
||||
private readonly ConfigurationGenerator $configGenerator
|
||||
) {}
|
||||
|
||||
public function deploy(Application $application): ApplicationDeploymentQueue
|
||||
{
|
||||
return DB::transaction(function () use ($application) {
|
||||
$deployment = $application->deployments()->create([
|
||||
'status' => 'queued',
|
||||
'commit_sha' => $application->getLatestCommitSha(),
|
||||
]);
|
||||
|
||||
DeployApplicationJob::dispatch($deployment);
|
||||
|
||||
return $deployment;
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Laravel Best Practices
|
||||
```php
|
||||
// Use Laravel conventions
|
||||
class Application extends Model
|
||||
{
|
||||
// Mass assignment protection
|
||||
protected $fillable = [
|
||||
'name', 'git_repository', 'git_branch', 'fqdn'
|
||||
];
|
||||
|
||||
// Type casting
|
||||
protected $casts = [
|
||||
'environment_variables' => 'array',
|
||||
'build_pack' => BuildPack::class,
|
||||
'created_at' => 'datetime',
|
||||
];
|
||||
|
||||
// Relationships
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
public function deployments(): HasMany
|
||||
{
|
||||
return $this->hasMany(ApplicationDeploymentQueue::class);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Frontend Standards
|
||||
```javascript
|
||||
// Alpine.js component structure
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.data('deploymentMonitor', () => ({
|
||||
status: 'idle',
|
||||
logs: [],
|
||||
|
||||
init() {
|
||||
this.connectWebSocket();
|
||||
},
|
||||
|
||||
connectWebSocket() {
|
||||
Echo.private(`application.${this.applicationId}`)
|
||||
.listen('DeploymentStarted', (e) => {
|
||||
this.status = 'deploying';
|
||||
})
|
||||
.listen('DeploymentCompleted', (e) => {
|
||||
this.status = 'completed';
|
||||
});
|
||||
}
|
||||
}));
|
||||
});
|
||||
```
|
||||
|
||||
### CSS/Tailwind Standards
|
||||
```html
|
||||
<!-- Use semantic class names and consistent spacing -->
|
||||
<div class="bg-white dark:bg-gray-900 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
||||
Application Status
|
||||
</h3>
|
||||
<div class="space-y-3">
|
||||
<!-- Content with consistent spacing -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Database Development
|
||||
|
||||
### Migration Best Practices
|
||||
```php
|
||||
// Create descriptive migration files
|
||||
class CreateApplicationDeploymentQueuesTable extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('application_deployment_queues', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('application_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('status')->default('queued');
|
||||
$table->string('commit_sha')->nullable();
|
||||
$table->text('build_logs')->nullable();
|
||||
$table->text('deployment_logs')->nullable();
|
||||
$table->timestamp('started_at')->nullable();
|
||||
$table->timestamp('finished_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['application_id', 'status']);
|
||||
$table->index('created_at');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('application_deployment_queues');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Model Factory Development
|
||||
```php
|
||||
// Create comprehensive factories for testing
|
||||
class ApplicationFactory extends Factory
|
||||
{
|
||||
protected $model = Application::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->words(2, true),
|
||||
'fqdn' => $this->faker->domainName,
|
||||
'git_repository' => 'https://github.com/' . $this->faker->userName . '/' . $this->faker->word . '.git',
|
||||
'git_branch' => 'main',
|
||||
'build_pack' => BuildPack::NIXPACKS,
|
||||
'server_id' => Server::factory(),
|
||||
'environment_id' => Environment::factory(),
|
||||
];
|
||||
}
|
||||
|
||||
public function withCustomDomain(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'fqdn' => $this->faker->domainName,
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Development
|
||||
|
||||
### Controller Standards
|
||||
```php
|
||||
class ApplicationController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth:sanctum');
|
||||
$this->middleware('team.access');
|
||||
}
|
||||
|
||||
public function index(Request $request): AnonymousResourceCollection
|
||||
{
|
||||
$applications = $request->user()
|
||||
->currentTeam
|
||||
->applications()
|
||||
->with(['server', 'environment', 'latestDeployment'])
|
||||
->paginate();
|
||||
|
||||
return ApplicationResource::collection($applications);
|
||||
}
|
||||
|
||||
public function store(StoreApplicationRequest $request): ApplicationResource
|
||||
{
|
||||
$application = $request->user()
|
||||
->currentTeam
|
||||
->applications()
|
||||
->create($request->validated());
|
||||
|
||||
return new ApplicationResource($application);
|
||||
}
|
||||
|
||||
public function deploy(Application $application): JsonResponse
|
||||
{
|
||||
$this->authorize('deploy', $application);
|
||||
|
||||
$deployment = app(ApplicationDeploymentService::class)
|
||||
->deploy($application);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Deployment started successfully',
|
||||
'deployment_id' => $deployment->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### API Resource Development
|
||||
```php
|
||||
class ApplicationResource extends JsonResource
|
||||
{
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'fqdn' => $this->fqdn,
|
||||
'status' => $this->status,
|
||||
'git_repository' => $this->git_repository,
|
||||
'git_branch' => $this->git_branch,
|
||||
'build_pack' => $this->build_pack,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
|
||||
// Conditional relationships
|
||||
'server' => new ServerResource($this->whenLoaded('server')),
|
||||
'environment' => new EnvironmentResource($this->whenLoaded('environment')),
|
||||
'latest_deployment' => new DeploymentResource($this->whenLoaded('latestDeployment')),
|
||||
|
||||
// Computed attributes
|
||||
'deployment_url' => $this->getDeploymentUrl(),
|
||||
'can_deploy' => $this->canDeploy(),
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Livewire Component Development
|
||||
|
||||
### Component Structure
|
||||
```php
|
||||
class ApplicationShow extends Component
|
||||
{
|
||||
public Application $application;
|
||||
public bool $showLogs = false;
|
||||
|
||||
protected $listeners = [
|
||||
'deployment.started' => 'refreshDeploymentStatus',
|
||||
'deployment.completed' => 'refreshDeploymentStatus',
|
||||
];
|
||||
|
||||
public function mount(Application $application): void
|
||||
{
|
||||
$this->authorize('view', $application);
|
||||
$this->application = $application;
|
||||
}
|
||||
|
||||
public function deploy(): void
|
||||
{
|
||||
$this->authorize('deploy', $this->application);
|
||||
|
||||
try {
|
||||
app(ApplicationDeploymentService::class)->deploy($this->application);
|
||||
|
||||
$this->dispatch('deployment.started', [
|
||||
'application_id' => $this->application->id
|
||||
]);
|
||||
|
||||
session()->flash('success', 'Deployment started successfully');
|
||||
} catch (Exception $e) {
|
||||
session()->flash('error', 'Failed to start deployment: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function refreshDeploymentStatus(): void
|
||||
{
|
||||
$this->application->refresh();
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.application.show', [
|
||||
'deployments' => $this->application
|
||||
->deployments()
|
||||
->latest()
|
||||
->limit(10)
|
||||
->get()
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Queue Job Development
|
||||
|
||||
### Job Structure
|
||||
```php
|
||||
class DeployApplicationJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public int $tries = 3;
|
||||
public int $maxExceptions = 1;
|
||||
|
||||
public function __construct(
|
||||
public ApplicationDeploymentQueue $deployment
|
||||
) {}
|
||||
|
||||
public function handle(
|
||||
DockerService $dockerService,
|
||||
ConfigurationGenerator $configGenerator
|
||||
): void {
|
||||
$this->deployment->update(['status' => 'running', 'started_at' => now()]);
|
||||
|
||||
try {
|
||||
// Generate configuration
|
||||
$config = $configGenerator->generateDockerCompose($this->deployment->application);
|
||||
|
||||
// Build and deploy
|
||||
$imageTag = $dockerService->buildImage($this->deployment->application);
|
||||
$dockerService->deployContainer($this->deployment->application, $imageTag);
|
||||
|
||||
$this->deployment->update([
|
||||
'status' => 'success',
|
||||
'finished_at' => now()
|
||||
]);
|
||||
|
||||
// Broadcast success
|
||||
broadcast(new DeploymentCompleted($this->deployment));
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->deployment->update([
|
||||
'status' => 'failed',
|
||||
'error_message' => $e->getMessage(),
|
||||
'finished_at' => now()
|
||||
]);
|
||||
|
||||
broadcast(new DeploymentFailed($this->deployment));
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function backoff(): array
|
||||
{
|
||||
return [1, 5, 10];
|
||||
}
|
||||
|
||||
public function failed(Throwable $exception): void
|
||||
{
|
||||
$this->deployment->update([
|
||||
'status' => 'failed',
|
||||
'error_message' => $exception->getMessage(),
|
||||
'finished_at' => now()
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Development
|
||||
|
||||
### Test Structure
|
||||
```php
|
||||
// Feature test example
|
||||
test('user can deploy application via API', function () {
|
||||
$user = User::factory()->create();
|
||||
$application = Application::factory()->create([
|
||||
'team_id' => $user->currentTeam->id
|
||||
]);
|
||||
|
||||
// Mock external services
|
||||
$this->mock(DockerService::class, function ($mock) {
|
||||
$mock->shouldReceive('buildImage')->andReturn('app:latest');
|
||||
$mock->shouldReceive('deployContainer')->andReturn(true);
|
||||
});
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->postJson("/api/v1/applications/{$application->id}/deploy");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJson([
|
||||
'message' => 'Deployment started successfully'
|
||||
]);
|
||||
|
||||
expect($application->deployments()->count())->toBe(1);
|
||||
expect($application->deployments()->first()->status)->toBe('queued');
|
||||
});
|
||||
```
|
||||
|
||||
## Documentation Standards
|
||||
|
||||
### Code Documentation
|
||||
```php
|
||||
/**
|
||||
* Deploy an application to the specified server.
|
||||
*
|
||||
* This method creates a new deployment queue entry and dispatches
|
||||
* a background job to handle the actual deployment process.
|
||||
*
|
||||
* @param Application $application The application to deploy
|
||||
* @param array $options Additional deployment options
|
||||
* @return ApplicationDeploymentQueue The created deployment queue entry
|
||||
*
|
||||
* @throws DeploymentException When deployment cannot be started
|
||||
* @throws ServerConnectionException When server is unreachable
|
||||
*/
|
||||
public function deploy(Application $application, array $options = []): ApplicationDeploymentQueue
|
||||
{
|
||||
// Implementation
|
||||
}
|
||||
```
|
||||
|
||||
### API Documentation
|
||||
```php
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/applications/{application}/deploy",
|
||||
* summary="Deploy an application",
|
||||
* description="Triggers a new deployment for the specified application",
|
||||
* operationId="deployApplication",
|
||||
* tags={"Applications"},
|
||||
* security={{"bearerAuth":{}}},
|
||||
* @OA\Parameter(
|
||||
* name="application",
|
||||
* in="path",
|
||||
* required=true,
|
||||
* @OA\Schema(type="integer"),
|
||||
* description="Application ID"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Deployment started successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="message", type="string"),
|
||||
* @OA\Property(property="deployment_id", type="integer")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Database Optimization
|
||||
```php
|
||||
// Use eager loading to prevent N+1 queries
|
||||
$applications = Application::with([
|
||||
'server:id,name,ip',
|
||||
'environment:id,name',
|
||||
'latestDeployment:id,application_id,status,created_at'
|
||||
])->get();
|
||||
|
||||
// Use database transactions for consistency
|
||||
DB::transaction(function () use ($application) {
|
||||
$deployment = $application->deployments()->create(['status' => 'queued']);
|
||||
$application->update(['last_deployment_at' => now()]);
|
||||
DeployApplicationJob::dispatch($deployment);
|
||||
});
|
||||
```
|
||||
|
||||
### Caching Strategies
|
||||
```php
|
||||
// Cache expensive operations
|
||||
public function getServerMetrics(Server $server): array
|
||||
{
|
||||
return Cache::remember(
|
||||
"server.{$server->id}.metrics",
|
||||
now()->addMinutes(5),
|
||||
fn () => $this->fetchServerMetrics($server)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Deployment & Release Process
|
||||
|
||||
### Version Management
|
||||
- **[versions.json](mdc:versions.json)** - Version tracking (355B, 19 lines)
|
||||
- **[CHANGELOG.md](mdc:CHANGELOG.md)** - Release notes (187KB, 7411 lines)
|
||||
- **[cliff.toml](mdc:cliff.toml)** - Changelog generation (3.2KB, 85 lines)
|
||||
|
||||
### Release Workflow
|
||||
```bash
|
||||
# Create release branch
|
||||
git checkout -b release/v4.1.0
|
||||
|
||||
# Update version numbers
|
||||
# Update CHANGELOG.md
|
||||
# Run full test suite
|
||||
./vendor/bin/pest
|
||||
npm run test
|
||||
|
||||
# Create release commit
|
||||
git commit -m "chore: release v4.1.0"
|
||||
|
||||
# Create and push tag
|
||||
git tag v4.1.0
|
||||
git push origin v4.1.0
|
||||
|
||||
# Merge to main
|
||||
git checkout main
|
||||
git merge release/v4.1.0
|
||||
```
|
||||
|
||||
## Contributing Guidelines
|
||||
|
||||
### Pull Request Process
|
||||
1. **Fork** the repository
|
||||
2. **Create** feature branch from `main`
|
||||
3. **Implement** changes with tests
|
||||
4. **Run** code quality checks
|
||||
5. **Submit** pull request with clear description
|
||||
6. **Address** review feedback
|
||||
7. **Merge** after approval
|
||||
|
||||
### Code Review Checklist
|
||||
- [ ] Code follows project standards
|
||||
- [ ] Tests cover new functionality
|
||||
- [ ] Documentation is updated
|
||||
- [ ] No breaking changes without migration
|
||||
- [ ] Performance impact considered
|
||||
- [ ] Security implications reviewed
|
||||
|
||||
### Issue Reporting
|
||||
- Use issue templates
|
||||
- Provide reproduction steps
|
||||
- Include environment details
|
||||
- Add relevant logs/screenshots
|
||||
- Label appropriately
|
319
.cursor/rules/frontend-patterns.mdc
Normal file
319
.cursor/rules/frontend-patterns.mdc
Normal file
@@ -0,0 +1,319 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Coolify Frontend Architecture & Patterns
|
||||
|
||||
## Frontend Philosophy
|
||||
|
||||
Coolify uses a **server-side first** approach with minimal JavaScript, leveraging Livewire for reactivity and Alpine.js for lightweight client-side interactions.
|
||||
|
||||
## Core Frontend Stack
|
||||
|
||||
### Livewire 3.5+ (Primary Framework)
|
||||
- **Server-side rendering** with reactive components
|
||||
- **Real-time updates** without page refreshes
|
||||
- **State management** handled on the server
|
||||
- **WebSocket integration** for live updates
|
||||
|
||||
### Alpine.js (Client-Side Interactivity)
|
||||
- **Lightweight JavaScript** for DOM manipulation
|
||||
- **Declarative directives** in HTML
|
||||
- **Component-like behavior** without build steps
|
||||
- **Perfect companion** to Livewire
|
||||
|
||||
### Tailwind CSS 4.1+ (Styling)
|
||||
- **Utility-first** CSS framework
|
||||
- **Custom design system** for deployment platform
|
||||
- **Responsive design** built-in
|
||||
- **Dark mode support**
|
||||
|
||||
## Livewire Component Structure
|
||||
|
||||
### Location: [app/Livewire/](mdc:app/Livewire)
|
||||
|
||||
#### Core Application Components
|
||||
- **[Dashboard.php](mdc:app/Livewire/Dashboard.php)** - Main dashboard interface
|
||||
- **[ActivityMonitor.php](mdc:app/Livewire/ActivityMonitor.php)** - Real-time activity tracking
|
||||
- **[MonacoEditor.php](mdc:app/Livewire/MonacoEditor.php)** - Code editor component
|
||||
|
||||
#### Server Management
|
||||
- **Server/** directory - Server configuration and monitoring
|
||||
- Real-time server status updates
|
||||
- SSH connection management
|
||||
- Resource monitoring
|
||||
|
||||
#### Project & Application Management
|
||||
- **Project/** directory - Project organization
|
||||
- Application deployment interfaces
|
||||
- Environment variable management
|
||||
- Service configuration
|
||||
|
||||
#### Settings & Configuration
|
||||
- **Settings/** directory - System configuration
|
||||
- **[SettingsEmail.php](mdc:app/Livewire/SettingsEmail.php)** - Email notification setup
|
||||
- **[SettingsOauth.php](mdc:app/Livewire/SettingsOauth.php)** - OAuth provider configuration
|
||||
- **[SettingsBackup.php](mdc:app/Livewire/SettingsBackup.php)** - Backup configuration
|
||||
|
||||
#### User & Team Management
|
||||
- **Team/** directory - Team collaboration features
|
||||
- **Profile/** directory - User profile management
|
||||
- **Security/** directory - Security settings
|
||||
|
||||
## Blade Template Organization
|
||||
|
||||
### Location: [resources/views/](mdc:resources/views)
|
||||
|
||||
#### Layout Structure
|
||||
- **layouts/** - Base layout templates
|
||||
- **components/** - Reusable UI components
|
||||
- **livewire/** - Livewire component views
|
||||
|
||||
#### Feature-Specific Views
|
||||
- **server/** - Server management interfaces
|
||||
- **auth/** - Authentication pages
|
||||
- **emails/** - Email templates
|
||||
- **errors/** - Error pages
|
||||
|
||||
## Interactive Components
|
||||
|
||||
### Monaco Editor Integration
|
||||
- **Code editing** for configuration files
|
||||
- **Syntax highlighting** for multiple languages
|
||||
- **Live validation** and error detection
|
||||
- **Integration** with deployment process
|
||||
|
||||
### Terminal Emulation (XTerm.js)
|
||||
- **Real-time terminal** access to servers
|
||||
- **WebSocket-based** communication
|
||||
- **Multi-session** support
|
||||
- **Secure connection** through SSH
|
||||
|
||||
### Real-Time Updates
|
||||
- **WebSocket connections** via Laravel Echo
|
||||
- **Live deployment logs** streaming
|
||||
- **Server monitoring** with live metrics
|
||||
- **Activity notifications** in real-time
|
||||
|
||||
## Alpine.js Patterns
|
||||
|
||||
### Common Directives Used
|
||||
```html
|
||||
<!-- State management -->
|
||||
<div x-data="{ open: false }">
|
||||
|
||||
<!-- Event handling -->
|
||||
<button x-on:click="open = !open">
|
||||
|
||||
<!-- Conditional rendering -->
|
||||
<div x-show="open">
|
||||
|
||||
<!-- Data binding -->
|
||||
<input x-model="searchTerm">
|
||||
|
||||
<!-- Component initialization -->
|
||||
<div x-init="initializeComponent()">
|
||||
```
|
||||
|
||||
### Integration with Livewire
|
||||
```html
|
||||
<!-- Livewire actions with Alpine state -->
|
||||
<button
|
||||
x-data="{ loading: false }"
|
||||
x-on:click="loading = true"
|
||||
wire:click="deploy"
|
||||
wire:loading.attr="disabled"
|
||||
wire:target="deploy"
|
||||
>
|
||||
<span x-show="!loading">Deploy</span>
|
||||
<span x-show="loading">Deploying...</span>
|
||||
</button>
|
||||
```
|
||||
|
||||
## Tailwind CSS Patterns
|
||||
|
||||
### Design System
|
||||
- **Consistent spacing** using Tailwind scale
|
||||
- **Color palette** optimized for deployment platform
|
||||
- **Typography** hierarchy for technical content
|
||||
- **Component classes** for reusable elements
|
||||
|
||||
### Responsive Design
|
||||
```html
|
||||
<!-- Mobile-first responsive design -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
|
||||
<!-- Content adapts to screen size -->
|
||||
</div>
|
||||
```
|
||||
|
||||
### Dark Mode Support
|
||||
```html
|
||||
<!-- Dark mode variants -->
|
||||
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
|
||||
<!-- Automatic dark mode switching -->
|
||||
</div>
|
||||
```
|
||||
|
||||
## Build Process
|
||||
|
||||
### Vite Configuration ([vite.config.js](mdc:vite.config.js))
|
||||
- **Fast development** with hot module replacement
|
||||
- **Optimized production** builds
|
||||
- **Asset versioning** for cache busting
|
||||
- **CSS processing** with PostCSS
|
||||
|
||||
### Asset Compilation
|
||||
```bash
|
||||
# Development
|
||||
npm run dev
|
||||
|
||||
# Production build
|
||||
npm run build
|
||||
```
|
||||
|
||||
## State Management Patterns
|
||||
|
||||
### Server-Side State (Livewire)
|
||||
- **Component properties** for persistent state
|
||||
- **Session storage** for user preferences
|
||||
- **Database models** for application state
|
||||
- **Cache layer** for performance
|
||||
|
||||
### Client-Side State (Alpine.js)
|
||||
- **Local component state** for UI interactions
|
||||
- **Form validation** and user feedback
|
||||
- **Modal and dropdown** state management
|
||||
- **Temporary UI states** (loading, hover, etc.)
|
||||
|
||||
## Real-Time Features
|
||||
|
||||
### WebSocket Integration
|
||||
```php
|
||||
// Livewire component with real-time updates
|
||||
class ActivityMonitor extends Component
|
||||
{
|
||||
public function getListeners()
|
||||
{
|
||||
return [
|
||||
'deployment.started' => 'refresh',
|
||||
'deployment.finished' => 'refresh',
|
||||
'server.status.changed' => 'updateServerStatus',
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Event Broadcasting
|
||||
- **Laravel Echo** for client-side WebSocket handling
|
||||
- **Pusher protocol** for real-time communication
|
||||
- **Private channels** for user-specific events
|
||||
- **Presence channels** for collaborative features
|
||||
|
||||
## Performance Patterns
|
||||
|
||||
### Lazy Loading
|
||||
```php
|
||||
// Livewire lazy loading
|
||||
class ServerList extends Component
|
||||
{
|
||||
public function placeholder()
|
||||
{
|
||||
return view('components.loading-skeleton');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Caching Strategies
|
||||
- **Fragment caching** for expensive operations
|
||||
- **Image optimization** with lazy loading
|
||||
- **Asset bundling** and compression
|
||||
- **CDN integration** for static assets
|
||||
|
||||
## Form Handling Patterns
|
||||
|
||||
### Livewire Forms
|
||||
```php
|
||||
class ServerCreateForm extends Component
|
||||
{
|
||||
public $name;
|
||||
public $ip;
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|min:3',
|
||||
'ip' => 'required|ip',
|
||||
];
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->validate();
|
||||
// Save logic
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Real-Time Validation
|
||||
- **Live validation** as user types
|
||||
- **Server-side validation** rules
|
||||
- **Error message** display
|
||||
- **Success feedback** patterns
|
||||
|
||||
## Component Communication
|
||||
|
||||
### Parent-Child Communication
|
||||
```php
|
||||
// Parent component
|
||||
$this->emit('serverCreated', $server->id);
|
||||
|
||||
// Child component
|
||||
protected $listeners = ['serverCreated' => 'refresh'];
|
||||
```
|
||||
|
||||
### Cross-Component Events
|
||||
- **Global events** for application-wide updates
|
||||
- **Scoped events** for feature-specific communication
|
||||
- **Browser events** for JavaScript integration
|
||||
|
||||
## Error Handling & UX
|
||||
|
||||
### Loading States
|
||||
- **Skeleton screens** during data loading
|
||||
- **Progress indicators** for long operations
|
||||
- **Optimistic updates** with rollback capability
|
||||
|
||||
### Error Display
|
||||
- **Toast notifications** for user feedback
|
||||
- **Inline validation** errors
|
||||
- **Global error** handling
|
||||
- **Retry mechanisms** for failed operations
|
||||
|
||||
## Accessibility Patterns
|
||||
|
||||
### ARIA Labels and Roles
|
||||
```html
|
||||
<button
|
||||
aria-label="Deploy application"
|
||||
aria-describedby="deploy-help"
|
||||
wire:click="deploy"
|
||||
>
|
||||
Deploy
|
||||
</button>
|
||||
```
|
||||
|
||||
### Keyboard Navigation
|
||||
- **Tab order** management
|
||||
- **Keyboard shortcuts** for power users
|
||||
- **Focus management** in modals and forms
|
||||
- **Screen reader** compatibility
|
||||
|
||||
## Mobile Optimization
|
||||
|
||||
### Touch-Friendly Interface
|
||||
- **Larger tap targets** for mobile devices
|
||||
- **Swipe gestures** where appropriate
|
||||
- **Mobile-optimized** forms and navigation
|
||||
|
||||
### Progressive Enhancement
|
||||
- **Core functionality** works without JavaScript
|
||||
- **Enhanced experience** with JavaScript enabled
|
||||
- **Offline capabilities** where possible
|
161
.cursor/rules/project-overview.mdc
Normal file
161
.cursor/rules/project-overview.mdc
Normal file
@@ -0,0 +1,161 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Coolify Project Overview
|
||||
|
||||
## What is Coolify?
|
||||
|
||||
Coolify is an **open-source & self-hostable alternative to Heroku / Netlify / Vercel**. It's a comprehensive deployment platform that helps you manage servers, applications, and databases on your own hardware with just an SSH connection.
|
||||
|
||||
## Core Mission
|
||||
|
||||
**"Imagine having the ease of a cloud but with your own servers. That is Coolify."**
|
||||
|
||||
- **No vendor lock-in** - All configurations saved to your servers
|
||||
- **Self-hosted** - Complete control over your infrastructure
|
||||
- **SSH-only requirement** - Works with VPS, Bare Metal, Raspberry PIs, anything
|
||||
- **Docker-first** - Container-based deployment architecture
|
||||
|
||||
## Key Features
|
||||
|
||||
### 🚀 **Application Deployment**
|
||||
- Git-based deployments (GitHub, GitLab, Bitbucket, Gitea)
|
||||
- Docker & Docker Compose support
|
||||
- Preview deployments for pull requests
|
||||
- Zero-downtime deployments
|
||||
- Build cache optimization
|
||||
|
||||
### 🖥️ **Server Management**
|
||||
- Multi-server orchestration
|
||||
- Real-time monitoring and logs
|
||||
- SSH key management
|
||||
- Proxy configuration (Traefik/Caddy)
|
||||
- Resource usage tracking
|
||||
|
||||
### 🗄️ **Database Management**
|
||||
- PostgreSQL, MySQL, MariaDB, MongoDB
|
||||
- Redis, KeyDB, Dragonfly, ClickHouse
|
||||
- Automated backups with S3 integration
|
||||
- Database clustering support
|
||||
|
||||
### 🔧 **Infrastructure as Code**
|
||||
- Docker Compose generation
|
||||
- Environment variable management
|
||||
- SSL certificate automation
|
||||
- Custom domain configuration
|
||||
|
||||
### 👥 **Team Collaboration**
|
||||
- Multi-tenant team organization
|
||||
- Role-based access control
|
||||
- Project and environment isolation
|
||||
- Team-wide resource sharing
|
||||
|
||||
### 📊 **Monitoring & Observability**
|
||||
- Real-time application logs
|
||||
- Server resource monitoring
|
||||
- Deployment status tracking
|
||||
- Webhook integrations
|
||||
- Notification systems (Email, Discord, Slack, Telegram)
|
||||
|
||||
## Target Users
|
||||
|
||||
### **DevOps Engineers**
|
||||
- Infrastructure automation
|
||||
- Multi-environment management
|
||||
- CI/CD pipeline integration
|
||||
|
||||
### **Developers**
|
||||
- Easy application deployment
|
||||
- Development environment provisioning
|
||||
- Preview deployments for testing
|
||||
|
||||
### **Small to Medium Businesses**
|
||||
- Cost-effective Heroku alternative
|
||||
- Self-hosted control and privacy
|
||||
- Scalable infrastructure management
|
||||
|
||||
### **Agencies & Consultants**
|
||||
- Client project isolation
|
||||
- Multi-tenant management
|
||||
- White-label deployment solutions
|
||||
|
||||
## Business Model
|
||||
|
||||
### **Open Source (Free)**
|
||||
- Complete feature set
|
||||
- Self-hosted deployment
|
||||
- Community support
|
||||
- No feature restrictions
|
||||
|
||||
### **Cloud Version (Paid)**
|
||||
- Managed Coolify instance
|
||||
- High availability
|
||||
- Premium support
|
||||
- Email notifications included
|
||||
- Same price as self-hosted server (~$4-5/month)
|
||||
|
||||
## Architecture Philosophy
|
||||
|
||||
### **Server-Side First**
|
||||
- Laravel backend with Livewire frontend
|
||||
- Minimal JavaScript footprint
|
||||
- Real-time updates via WebSockets
|
||||
- Progressive enhancement approach
|
||||
|
||||
### **Docker-Native**
|
||||
- Container-first deployment strategy
|
||||
- Docker Compose orchestration
|
||||
- Image building and registry integration
|
||||
- Volume and network management
|
||||
|
||||
### **Security-Focused**
|
||||
- SSH-based server communication
|
||||
- Environment variable encryption
|
||||
- Team-based access isolation
|
||||
- Audit logging and activity tracking
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
coolify/
|
||||
├── app/ # Laravel application core
|
||||
│ ├── Models/ # Domain models (Application, Server, Service)
|
||||
│ ├── Livewire/ # Frontend components
|
||||
│ ├── Actions/ # Business logic actions
|
||||
│ └── Jobs/ # Background job processing
|
||||
├── resources/ # Frontend assets and views
|
||||
├── database/ # Migrations and seeders
|
||||
├── docker/ # Docker configuration
|
||||
├── scripts/ # Installation and utility scripts
|
||||
└── tests/ # Test suites (Pest, Dusk)
|
||||
```
|
||||
|
||||
## Key Differentiators
|
||||
|
||||
### **vs. Heroku**
|
||||
- ✅ Self-hosted (no vendor lock-in)
|
||||
- ✅ Multi-server support
|
||||
- ✅ No usage-based pricing
|
||||
- ✅ Full infrastructure control
|
||||
|
||||
### **vs. Vercel/Netlify**
|
||||
- ✅ Backend application support
|
||||
- ✅ Database management included
|
||||
- ✅ Multi-environment workflows
|
||||
- ✅ Custom server infrastructure
|
||||
|
||||
### **vs. Docker Swarm/Kubernetes**
|
||||
- ✅ User-friendly web interface
|
||||
- ✅ Git-based deployment workflows
|
||||
- ✅ Integrated monitoring and logging
|
||||
- ✅ No complex YAML configuration
|
||||
|
||||
## Development Principles
|
||||
|
||||
- **Simplicity over complexity**
|
||||
- **Convention over configuration**
|
||||
- **Security by default**
|
||||
- **Developer experience focused**
|
||||
- **Community-driven development**
|
784
.cursor/rules/security-patterns.mdc
Normal file
784
.cursor/rules/security-patterns.mdc
Normal file
@@ -0,0 +1,784 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Coolify Security Architecture & Patterns
|
||||
|
||||
## Security Philosophy
|
||||
|
||||
Coolify implements **defense-in-depth security** with multiple layers of protection including authentication, authorization, encryption, network isolation, and secure deployment practices.
|
||||
|
||||
## Authentication Architecture
|
||||
|
||||
### Multi-Provider Authentication
|
||||
- **[Laravel Fortify](mdc:config/fortify.php)** - Core authentication scaffolding (4.9KB, 149 lines)
|
||||
- **[Laravel Sanctum](mdc:config/sanctum.php)** - API token authentication (2.4KB, 69 lines)
|
||||
- **[Laravel Socialite](mdc:config/services.php)** - OAuth provider integration
|
||||
|
||||
### OAuth Integration
|
||||
- **[OauthSetting.php](mdc:app/Models/OauthSetting.php)** - OAuth provider configurations
|
||||
- **Supported Providers**:
|
||||
- Google OAuth
|
||||
- Microsoft Azure AD
|
||||
- Authentik
|
||||
- GitHub (via GitHub Apps)
|
||||
- GitLab
|
||||
|
||||
### Authentication Models
|
||||
```php
|
||||
// User authentication with team-based access
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasApiTokens, HasFactory, Notifiable;
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'email', 'password'
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'password', 'remember_token'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
|
||||
public function teams(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Team::class)
|
||||
->withPivot('role')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function currentTeam(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Team::class, 'current_team_id');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Authorization & Access Control
|
||||
|
||||
### Team-Based Multi-Tenancy
|
||||
- **[Team.php](mdc:app/Models/Team.php)** - Multi-tenant organization structure (8.9KB, 308 lines)
|
||||
- **[TeamInvitation.php](mdc:app/Models/TeamInvitation.php)** - Secure team collaboration
|
||||
- **Role-based permissions** within teams
|
||||
- **Resource isolation** by team ownership
|
||||
|
||||
### Authorization Patterns
|
||||
```php
|
||||
// Team-scoped authorization middleware
|
||||
class EnsureTeamAccess
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$user = $request->user();
|
||||
$teamId = $request->route('team');
|
||||
|
||||
if (!$user->teams->contains('id', $teamId)) {
|
||||
abort(403, 'Access denied to team resources');
|
||||
}
|
||||
|
||||
// Set current team context
|
||||
$user->switchTeam($teamId);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
// Resource-level authorization policies
|
||||
class ApplicationPolicy
|
||||
{
|
||||
public function view(User $user, Application $application): bool
|
||||
{
|
||||
return $user->teams->contains('id', $application->team_id);
|
||||
}
|
||||
|
||||
public function deploy(User $user, Application $application): bool
|
||||
{
|
||||
return $this->view($user, $application) &&
|
||||
$user->hasTeamPermission($application->team_id, 'deploy');
|
||||
}
|
||||
|
||||
public function delete(User $user, Application $application): bool
|
||||
{
|
||||
return $this->view($user, $application) &&
|
||||
$user->hasTeamRole($application->team_id, 'admin');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Global Scopes for Data Isolation
|
||||
```php
|
||||
// Automatic team-based filtering
|
||||
class Application extends Model
|
||||
{
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::addGlobalScope('team', function (Builder $builder) {
|
||||
if (auth()->check() && auth()->user()->currentTeam) {
|
||||
$builder->whereHas('environment.project', function ($query) {
|
||||
$query->where('team_id', auth()->user()->currentTeam->id);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Security
|
||||
|
||||
### Token-Based Authentication
|
||||
```php
|
||||
// Sanctum API token management
|
||||
class PersonalAccessToken extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'name', 'token', 'abilities', 'expires_at'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'abilities' => 'array',
|
||||
'expires_at' => 'datetime',
|
||||
'last_used_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function tokenable(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public function hasAbility(string $ability): bool
|
||||
{
|
||||
return in_array('*', $this->abilities) ||
|
||||
in_array($ability, $this->abilities);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### API Rate Limiting
|
||||
```php
|
||||
// Rate limiting configuration
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
|
||||
});
|
||||
|
||||
RateLimiter::for('deployments', function (Request $request) {
|
||||
return Limit::perMinute(10)->by($request->user()->id);
|
||||
});
|
||||
|
||||
RateLimiter::for('webhooks', function (Request $request) {
|
||||
return Limit::perMinute(100)->by($request->ip());
|
||||
});
|
||||
```
|
||||
|
||||
### API Input Validation
|
||||
```php
|
||||
// Comprehensive input validation
|
||||
class StoreApplicationRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user()->can('create', Application::class);
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255|regex:/^[a-zA-Z0-9\-_]+$/',
|
||||
'git_repository' => 'required|url|starts_with:https://',
|
||||
'git_branch' => 'required|string|max:100|regex:/^[a-zA-Z0-9\-_\/]+$/',
|
||||
'server_id' => 'required|exists:servers,id',
|
||||
'environment_id' => 'required|exists:environments,id',
|
||||
'environment_variables' => 'array',
|
||||
'environment_variables.*' => 'string|max:1000',
|
||||
];
|
||||
}
|
||||
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
$this->merge([
|
||||
'name' => strip_tags($this->name),
|
||||
'git_repository' => filter_var($this->git_repository, FILTER_SANITIZE_URL),
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## SSH Security
|
||||
|
||||
### Private Key Management
|
||||
- **[PrivateKey.php](mdc:app/Models/PrivateKey.php)** - Secure SSH key storage (6.5KB, 247 lines)
|
||||
- **Encrypted key storage** in database
|
||||
- **Key rotation** capabilities
|
||||
- **Access logging** for key usage
|
||||
|
||||
### SSH Connection Security
|
||||
```php
|
||||
class SshConnection
|
||||
{
|
||||
private string $host;
|
||||
private int $port;
|
||||
private string $username;
|
||||
private PrivateKey $privateKey;
|
||||
|
||||
public function __construct(Server $server)
|
||||
{
|
||||
$this->host = $server->ip;
|
||||
$this->port = $server->port;
|
||||
$this->username = $server->user;
|
||||
$this->privateKey = $server->privateKey;
|
||||
}
|
||||
|
||||
public function connect(): bool
|
||||
{
|
||||
$connection = ssh2_connect($this->host, $this->port);
|
||||
|
||||
if (!$connection) {
|
||||
throw new SshConnectionException('Failed to connect to server');
|
||||
}
|
||||
|
||||
// Use private key authentication
|
||||
$privateKeyContent = decrypt($this->privateKey->private_key);
|
||||
$publicKeyContent = decrypt($this->privateKey->public_key);
|
||||
|
||||
if (!ssh2_auth_pubkey_file($connection, $this->username, $publicKeyContent, $privateKeyContent)) {
|
||||
throw new SshAuthenticationException('SSH authentication failed');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function execute(string $command): string
|
||||
{
|
||||
// Sanitize command to prevent injection
|
||||
$command = escapeshellcmd($command);
|
||||
|
||||
$stream = ssh2_exec($this->connection, $command);
|
||||
|
||||
if (!$stream) {
|
||||
throw new SshExecutionException('Failed to execute command');
|
||||
}
|
||||
|
||||
return stream_get_contents($stream);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Container Security
|
||||
|
||||
### Docker Security Patterns
|
||||
```php
|
||||
class DockerSecurityService
|
||||
{
|
||||
public function createSecureContainer(Application $application): array
|
||||
{
|
||||
return [
|
||||
'image' => $this->validateImageName($application->docker_image),
|
||||
'user' => '1000:1000', // Non-root user
|
||||
'read_only' => true,
|
||||
'no_new_privileges' => true,
|
||||
'security_opt' => [
|
||||
'no-new-privileges:true',
|
||||
'apparmor:docker-default'
|
||||
],
|
||||
'cap_drop' => ['ALL'],
|
||||
'cap_add' => ['CHOWN', 'SETUID', 'SETGID'], // Minimal capabilities
|
||||
'tmpfs' => [
|
||||
'/tmp' => 'rw,noexec,nosuid,size=100m',
|
||||
'/var/tmp' => 'rw,noexec,nosuid,size=50m'
|
||||
],
|
||||
'ulimits' => [
|
||||
'nproc' => 1024,
|
||||
'nofile' => 1024
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
private function validateImageName(string $image): string
|
||||
{
|
||||
// Validate image name against allowed registries
|
||||
$allowedRegistries = ['docker.io', 'ghcr.io', 'quay.io'];
|
||||
|
||||
$parser = new DockerImageParser();
|
||||
$parsed = $parser->parse($image);
|
||||
|
||||
if (!in_array($parsed['registry'], $allowedRegistries)) {
|
||||
throw new SecurityException('Image registry not allowed');
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Network Isolation
|
||||
```yaml
|
||||
# Docker Compose security configuration
|
||||
version: '3.8'
|
||||
services:
|
||||
app:
|
||||
image: ${APP_IMAGE}
|
||||
networks:
|
||||
- app-network
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
- apparmor:docker-default
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp:rw,noexec,nosuid,size=100m
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- CHOWN
|
||||
- SETUID
|
||||
- SETGID
|
||||
|
||||
networks:
|
||||
app-network:
|
||||
driver: bridge
|
||||
internal: true
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.20.0.0/16
|
||||
```
|
||||
|
||||
## SSL/TLS Security
|
||||
|
||||
### Certificate Management
|
||||
- **[SslCertificate.php](mdc:app/Models/SslCertificate.php)** - SSL certificate automation
|
||||
- **Let's Encrypt** integration for free certificates
|
||||
- **Automatic renewal** and monitoring
|
||||
- **Custom certificate** upload support
|
||||
|
||||
### SSL Configuration
|
||||
```php
|
||||
class SslCertificateService
|
||||
{
|
||||
public function generateCertificate(Application $application): SslCertificate
|
||||
{
|
||||
$domains = $this->validateDomains($application->getAllDomains());
|
||||
|
||||
$certificate = SslCertificate::create([
|
||||
'application_id' => $application->id,
|
||||
'domains' => $domains,
|
||||
'provider' => 'letsencrypt',
|
||||
'status' => 'pending'
|
||||
]);
|
||||
|
||||
// Generate certificate using ACME protocol
|
||||
$acmeClient = new AcmeClient();
|
||||
$certData = $acmeClient->generateCertificate($domains);
|
||||
|
||||
$certificate->update([
|
||||
'certificate' => encrypt($certData['certificate']),
|
||||
'private_key' => encrypt($certData['private_key']),
|
||||
'chain' => encrypt($certData['chain']),
|
||||
'expires_at' => $certData['expires_at'],
|
||||
'status' => 'active'
|
||||
]);
|
||||
|
||||
return $certificate;
|
||||
}
|
||||
|
||||
private function validateDomains(array $domains): array
|
||||
{
|
||||
foreach ($domains as $domain) {
|
||||
if (!filter_var($domain, FILTER_VALIDATE_DOMAIN)) {
|
||||
throw new InvalidDomainException("Invalid domain: {$domain}");
|
||||
}
|
||||
|
||||
// Check domain ownership
|
||||
if (!$this->verifyDomainOwnership($domain)) {
|
||||
throw new DomainOwnershipException("Domain ownership verification failed: {$domain}");
|
||||
}
|
||||
}
|
||||
|
||||
return $domains;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Environment Variable Security
|
||||
|
||||
### Secure Configuration Management
|
||||
```php
|
||||
class EnvironmentVariable extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'key', 'value', 'is_secret', 'application_id'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_secret' => 'boolean',
|
||||
'value' => 'encrypted' // Automatic encryption for sensitive values
|
||||
];
|
||||
|
||||
public function setValueAttribute($value): void
|
||||
{
|
||||
// Automatically encrypt sensitive environment variables
|
||||
if ($this->isSensitiveKey($this->key)) {
|
||||
$this->attributes['value'] = encrypt($value);
|
||||
$this->attributes['is_secret'] = true;
|
||||
} else {
|
||||
$this->attributes['value'] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function getValueAttribute($value): string
|
||||
{
|
||||
if ($this->is_secret) {
|
||||
return decrypt($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function isSensitiveKey(string $key): bool
|
||||
{
|
||||
$sensitivePatterns = [
|
||||
'PASSWORD', 'SECRET', 'KEY', 'TOKEN', 'API_KEY',
|
||||
'DATABASE_URL', 'REDIS_URL', 'PRIVATE'
|
||||
];
|
||||
|
||||
foreach ($sensitivePatterns as $pattern) {
|
||||
if (str_contains(strtoupper($key), $pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Webhook Security
|
||||
|
||||
### Webhook Signature Verification
|
||||
```php
|
||||
class WebhookSecurityService
|
||||
{
|
||||
public function verifyGitHubSignature(Request $request, string $secret): bool
|
||||
{
|
||||
$signature = $request->header('X-Hub-Signature-256');
|
||||
|
||||
if (!$signature) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$expectedSignature = 'sha256=' . hash_hmac('sha256', $request->getContent(), $secret);
|
||||
|
||||
return hash_equals($expectedSignature, $signature);
|
||||
}
|
||||
|
||||
public function verifyGitLabSignature(Request $request, string $secret): bool
|
||||
{
|
||||
$signature = $request->header('X-Gitlab-Token');
|
||||
|
||||
return hash_equals($secret, $signature);
|
||||
}
|
||||
|
||||
public function validateWebhookPayload(array $payload): array
|
||||
{
|
||||
// Sanitize and validate webhook payload
|
||||
$validator = Validator::make($payload, [
|
||||
'repository.clone_url' => 'required|url|starts_with:https://',
|
||||
'ref' => 'required|string|max:255',
|
||||
'head_commit.id' => 'required|string|size:40', // Git SHA
|
||||
'head_commit.message' => 'required|string|max:1000'
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
throw new InvalidWebhookPayloadException('Invalid webhook payload');
|
||||
}
|
||||
|
||||
return $validator->validated();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Input Sanitization & Validation
|
||||
|
||||
### XSS Prevention
|
||||
```php
|
||||
class SecurityMiddleware
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
// Sanitize input data
|
||||
$input = $request->all();
|
||||
$sanitized = $this->sanitizeInput($input);
|
||||
$request->merge($sanitized);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
private function sanitizeInput(array $input): array
|
||||
{
|
||||
foreach ($input as $key => $value) {
|
||||
if (is_string($value)) {
|
||||
// Remove potentially dangerous HTML tags
|
||||
$input[$key] = strip_tags($value, '<p><br><strong><em>');
|
||||
|
||||
// Escape special characters
|
||||
$input[$key] = htmlspecialchars($input[$key], ENT_QUOTES, 'UTF-8');
|
||||
} elseif (is_array($value)) {
|
||||
$input[$key] = $this->sanitizeInput($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### SQL Injection Prevention
|
||||
```php
|
||||
// Always use parameterized queries and Eloquent ORM
|
||||
class ApplicationRepository
|
||||
{
|
||||
public function findByName(string $name): ?Application
|
||||
{
|
||||
// Safe: Uses parameter binding
|
||||
return Application::where('name', $name)->first();
|
||||
}
|
||||
|
||||
public function searchApplications(string $query): Collection
|
||||
{
|
||||
// Safe: Eloquent handles escaping
|
||||
return Application::where('name', 'LIKE', "%{$query}%")
|
||||
->orWhere('description', 'LIKE', "%{$query}%")
|
||||
->get();
|
||||
}
|
||||
|
||||
// NEVER do this - vulnerable to SQL injection
|
||||
// public function unsafeSearch(string $query): Collection
|
||||
// {
|
||||
// return DB::select("SELECT * FROM applications WHERE name LIKE '%{$query}%'");
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
## Audit Logging & Monitoring
|
||||
|
||||
### Activity Logging
|
||||
```php
|
||||
// Using Spatie Activity Log package
|
||||
class Application extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
|
||||
protected static $logAttributes = [
|
||||
'name', 'git_repository', 'git_branch', 'fqdn'
|
||||
];
|
||||
|
||||
protected static $logOnlyDirty = true;
|
||||
|
||||
public function getDescriptionForEvent(string $eventName): string
|
||||
{
|
||||
return "Application {$this->name} was {$eventName}";
|
||||
}
|
||||
}
|
||||
|
||||
// Custom security events
|
||||
class SecurityEventLogger
|
||||
{
|
||||
public function logFailedLogin(string $email, string $ip): void
|
||||
{
|
||||
activity('security')
|
||||
->withProperties([
|
||||
'email' => $email,
|
||||
'ip' => $ip,
|
||||
'user_agent' => request()->userAgent()
|
||||
])
|
||||
->log('Failed login attempt');
|
||||
}
|
||||
|
||||
public function logSuspiciousActivity(User $user, string $activity): void
|
||||
{
|
||||
activity('security')
|
||||
->causedBy($user)
|
||||
->withProperties([
|
||||
'activity' => $activity,
|
||||
'ip' => request()->ip(),
|
||||
'timestamp' => now()
|
||||
])
|
||||
->log('Suspicious activity detected');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Security Monitoring
|
||||
```php
|
||||
class SecurityMonitoringService
|
||||
{
|
||||
public function detectAnomalousActivity(User $user): bool
|
||||
{
|
||||
// Check for unusual login patterns
|
||||
$recentLogins = $user->activities()
|
||||
->where('description', 'like', '%login%')
|
||||
->where('created_at', '>=', now()->subHours(24))
|
||||
->get();
|
||||
|
||||
// Multiple failed attempts
|
||||
$failedAttempts = $recentLogins->where('description', 'Failed login attempt')->count();
|
||||
if ($failedAttempts > 5) {
|
||||
$this->triggerSecurityAlert($user, 'Multiple failed login attempts');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Login from new location
|
||||
$uniqueIps = $recentLogins->pluck('properties.ip')->unique();
|
||||
if ($uniqueIps->count() > 3) {
|
||||
$this->triggerSecurityAlert($user, 'Login from multiple IP addresses');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function triggerSecurityAlert(User $user, string $reason): void
|
||||
{
|
||||
// Send security notification
|
||||
$user->notify(new SecurityAlertNotification($reason));
|
||||
|
||||
// Log security event
|
||||
activity('security')
|
||||
->causedBy($user)
|
||||
->withProperties(['reason' => $reason])
|
||||
->log('Security alert triggered');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Backup Security
|
||||
|
||||
### Encrypted Backups
|
||||
```php
|
||||
class SecureBackupService
|
||||
{
|
||||
public function createEncryptedBackup(ScheduledDatabaseBackup $backup): void
|
||||
{
|
||||
$database = $backup->database;
|
||||
$dumpPath = $this->createDatabaseDump($database);
|
||||
|
||||
// Encrypt backup file
|
||||
$encryptedPath = $this->encryptFile($dumpPath, $backup->encryption_key);
|
||||
|
||||
// Upload to secure storage
|
||||
$this->uploadToSecureStorage($encryptedPath, $backup->s3Storage);
|
||||
|
||||
// Clean up local files
|
||||
unlink($dumpPath);
|
||||
unlink($encryptedPath);
|
||||
}
|
||||
|
||||
private function encryptFile(string $filePath, string $key): string
|
||||
{
|
||||
$data = file_get_contents($filePath);
|
||||
$encryptedData = encrypt($data, $key);
|
||||
|
||||
$encryptedPath = $filePath . '.encrypted';
|
||||
file_put_contents($encryptedPath, $encryptedData);
|
||||
|
||||
return $encryptedPath;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Security Headers & CORS
|
||||
|
||||
### Security Headers Configuration
|
||||
```php
|
||||
// Security headers middleware
|
||||
class SecurityHeadersMiddleware
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$response = $next($request);
|
||||
|
||||
$response->headers->set('X-Content-Type-Options', 'nosniff');
|
||||
$response->headers->set('X-Frame-Options', 'DENY');
|
||||
$response->headers->set('X-XSS-Protection', '1; mode=block');
|
||||
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
$response->headers->set('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
|
||||
|
||||
if ($request->secure()) {
|
||||
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### CORS Configuration
|
||||
```php
|
||||
// CORS configuration for API endpoints
|
||||
return [
|
||||
'paths' => ['api/*', 'webhooks/*'],
|
||||
'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
|
||||
'allowed_origins' => [
|
||||
'https://app.coolify.io',
|
||||
'https://*.coolify.io'
|
||||
],
|
||||
'allowed_origins_patterns' => [],
|
||||
'allowed_headers' => ['*'],
|
||||
'exposed_headers' => [],
|
||||
'max_age' => 0,
|
||||
'supports_credentials' => true,
|
||||
];
|
||||
```
|
||||
|
||||
## Security Testing
|
||||
|
||||
### Security Test Patterns
|
||||
```php
|
||||
// Security-focused tests
|
||||
test('prevents SQL injection in search', function () {
|
||||
$user = User::factory()->create();
|
||||
$maliciousInput = "'; DROP TABLE applications; --";
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->getJson("/api/v1/applications?search={$maliciousInput}");
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
// Verify applications table still exists
|
||||
expect(Schema::hasTable('applications'))->toBeTrue();
|
||||
});
|
||||
|
||||
test('prevents XSS in application names', function () {
|
||||
$user = User::factory()->create();
|
||||
$xssPayload = '<script>alert("xss")</script>';
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->postJson('/api/v1/applications', [
|
||||
'name' => $xssPayload,
|
||||
'git_repository' => 'https://github.com/user/repo.git',
|
||||
'server_id' => Server::factory()->create()->id
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
});
|
||||
|
||||
test('enforces team isolation', function () {
|
||||
$user1 = User::factory()->create();
|
||||
$user2 = User::factory()->create();
|
||||
|
||||
$team1 = Team::factory()->create();
|
||||
$team2 = Team::factory()->create();
|
||||
|
||||
$user1->teams()->attach($team1);
|
||||
$user2->teams()->attach($team2);
|
||||
|
||||
$application = Application::factory()->create(['team_id' => $team1->id]);
|
||||
|
||||
$response = $this->actingAs($user2)
|
||||
->getJson("/api/v1/applications/{$application->id}");
|
||||
|
||||
$response->assertStatus(403);
|
||||
});
|
||||
```
|
72
.cursor/rules/self_improve.mdc
Normal file
72
.cursor/rules/self_improve.mdc
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
description: Guidelines for continuously improving Cursor rules based on emerging code patterns and best practices.
|
||||
globs: **/*
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
- **Rule Improvement Triggers:**
|
||||
- New code patterns not covered by existing rules
|
||||
- Repeated similar implementations across files
|
||||
- Common error patterns that could be prevented
|
||||
- New libraries or tools being used consistently
|
||||
- Emerging best practices in the codebase
|
||||
|
||||
- **Analysis Process:**
|
||||
- Compare new code with existing rules
|
||||
- Identify patterns that should be standardized
|
||||
- Look for references to external documentation
|
||||
- Check for consistent error handling patterns
|
||||
- Monitor test patterns and coverage
|
||||
|
||||
- **Rule Updates:**
|
||||
- **Add New Rules When:**
|
||||
- A new technology/pattern is used in 3+ files
|
||||
- Common bugs could be prevented by a rule
|
||||
- Code reviews repeatedly mention the same feedback
|
||||
- New security or performance patterns emerge
|
||||
|
||||
- **Modify Existing Rules When:**
|
||||
- Better examples exist in the codebase
|
||||
- Additional edge cases are discovered
|
||||
- Related rules have been updated
|
||||
- Implementation details have changed
|
||||
|
||||
- **Example Pattern Recognition:**
|
||||
```typescript
|
||||
// If you see repeated patterns like:
|
||||
const data = await prisma.user.findMany({
|
||||
select: { id: true, email: true },
|
||||
where: { status: 'ACTIVE' }
|
||||
});
|
||||
|
||||
// Consider adding to [prisma.mdc](mdc:.cursor/rules/prisma.mdc):
|
||||
// - Standard select fields
|
||||
// - Common where conditions
|
||||
// - Performance optimization patterns
|
||||
```
|
||||
|
||||
- **Rule Quality Checks:**
|
||||
- Rules should be actionable and specific
|
||||
- Examples should come from actual code
|
||||
- References should be up to date
|
||||
- Patterns should be consistently enforced
|
||||
|
||||
- **Continuous Improvement:**
|
||||
- Monitor code review comments
|
||||
- Track common development questions
|
||||
- Update rules after major refactors
|
||||
- Add links to relevant documentation
|
||||
- Cross-reference related rules
|
||||
|
||||
- **Rule Deprecation:**
|
||||
- Mark outdated patterns as deprecated
|
||||
- Remove rules that no longer apply
|
||||
- Update references to deprecated rules
|
||||
- Document migration paths for old patterns
|
||||
|
||||
- **Documentation Updates:**
|
||||
- Keep examples synchronized with code
|
||||
- Update references to external docs
|
||||
- Maintain links between related rules
|
||||
- Document breaking changes
|
||||
Follow [cursor_rules.mdc](mdc:.cursor/rules/cursor_rules.mdc) for proper rule formatting and structure.
|
250
.cursor/rules/technology-stack.mdc
Normal file
250
.cursor/rules/technology-stack.mdc
Normal file
@@ -0,0 +1,250 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Coolify Technology Stack
|
||||
|
||||
## Backend Framework
|
||||
|
||||
### **Laravel 12.4.1** (PHP Framework)
|
||||
- **Location**: [composer.json](mdc:composer.json)
|
||||
- **Purpose**: Core application framework
|
||||
- **Key Features**:
|
||||
- Eloquent ORM for database interactions
|
||||
- Artisan CLI for development tasks
|
||||
- Queue system for background jobs
|
||||
- Event-driven architecture
|
||||
|
||||
### **PHP 8.4**
|
||||
- **Requirement**: `^8.4` in [composer.json](mdc:composer.json)
|
||||
- **Features Used**:
|
||||
- Typed properties and return types
|
||||
- Attributes for validation and configuration
|
||||
- Match expressions
|
||||
- Constructor property promotion
|
||||
|
||||
## Frontend Stack
|
||||
|
||||
### **Livewire 3.5.20** (Primary Frontend Framework)
|
||||
- **Purpose**: Server-side rendering with reactive components
|
||||
- **Location**: [app/Livewire/](mdc:app/Livewire/)
|
||||
- **Key Components**:
|
||||
- [Dashboard.php](mdc:app/Livewire/Dashboard.php) - Main interface
|
||||
- [ActivityMonitor.php](mdc:app/Livewire/ActivityMonitor.php) - Real-time monitoring
|
||||
- [MonacoEditor.php](mdc:app/Livewire/MonacoEditor.php) - Code editor
|
||||
|
||||
### **Alpine.js** (Client-Side Interactivity)
|
||||
- **Purpose**: Lightweight JavaScript for DOM manipulation
|
||||
- **Integration**: Works seamlessly with Livewire components
|
||||
- **Usage**: Declarative directives in Blade templates
|
||||
|
||||
### **Tailwind CSS 4.1.4** (Styling Framework)
|
||||
- **Location**: [package.json](mdc:package.json)
|
||||
- **Configuration**: [postcss.config.cjs](mdc:postcss.config.cjs)
|
||||
- **Extensions**:
|
||||
- `@tailwindcss/forms` - Form styling
|
||||
- `@tailwindcss/typography` - Content typography
|
||||
- `tailwind-scrollbar` - Custom scrollbars
|
||||
|
||||
### **Vue.js 3.5.13** (Component Framework)
|
||||
- **Purpose**: Enhanced interactive components
|
||||
- **Integration**: Used alongside Livewire for complex UI
|
||||
- **Build Tool**: Vite with Vue plugin
|
||||
|
||||
## Database & Caching
|
||||
|
||||
### **PostgreSQL 15** (Primary Database)
|
||||
- **Purpose**: Main application data storage
|
||||
- **Features**: JSONB support, advanced indexing
|
||||
- **Models**: [app/Models/](mdc:app/Models/)
|
||||
|
||||
### **Redis 7** (Caching & Real-time)
|
||||
- **Purpose**:
|
||||
- Session storage
|
||||
- Queue backend
|
||||
- Real-time data caching
|
||||
- WebSocket session management
|
||||
|
||||
### **Supported Databases** (For User Applications)
|
||||
- **PostgreSQL**: [StandalonePostgresql.php](mdc:app/Models/StandalonePostgresql.php)
|
||||
- **MySQL**: [StandaloneMysql.php](mdc:app/Models/StandaloneMysql.php)
|
||||
- **MariaDB**: [StandaloneMariadb.php](mdc:app/Models/StandaloneMariadb.php)
|
||||
- **MongoDB**: [StandaloneMongodb.php](mdc:app/Models/StandaloneMongodb.php)
|
||||
- **Redis**: [StandaloneRedis.php](mdc:app/Models/StandaloneRedis.php)
|
||||
- **KeyDB**: [StandaloneKeydb.php](mdc:app/Models/StandaloneKeydb.php)
|
||||
- **Dragonfly**: [StandaloneDragonfly.php](mdc:app/Models/StandaloneDragonfly.php)
|
||||
- **ClickHouse**: [StandaloneClickhouse.php](mdc:app/Models/StandaloneClickhouse.php)
|
||||
|
||||
## Authentication & Security
|
||||
|
||||
### **Laravel Sanctum 4.0.8**
|
||||
- **Purpose**: API token authentication
|
||||
- **Usage**: Secure API access for external integrations
|
||||
|
||||
### **Laravel Fortify 1.25.4**
|
||||
- **Purpose**: Authentication scaffolding
|
||||
- **Features**: Login, registration, password reset
|
||||
|
||||
### **Laravel Socialite 5.18.0**
|
||||
- **Purpose**: OAuth provider integration
|
||||
- **Providers**:
|
||||
- GitHub, GitLab, Google
|
||||
- Microsoft Azure, Authentik
|
||||
- Custom OAuth implementations
|
||||
|
||||
## Background Processing
|
||||
|
||||
### **Laravel Horizon 5.30.3**
|
||||
- **Purpose**: Queue monitoring and management
|
||||
- **Features**: Real-time queue metrics, failed job handling
|
||||
|
||||
### **Queue System**
|
||||
- **Backend**: Redis-based queues
|
||||
- **Jobs**: [app/Jobs/](mdc:app/Jobs/)
|
||||
- **Processing**: Background deployment and monitoring tasks
|
||||
|
||||
## Development Tools
|
||||
|
||||
### **Build Tools**
|
||||
- **Vite 6.2.6**: Modern build tool and dev server
|
||||
- **Laravel Vite Plugin**: Laravel integration
|
||||
- **PostCSS**: CSS processing pipeline
|
||||
|
||||
### **Code Quality**
|
||||
- **Laravel Pint**: PHP code style fixer
|
||||
- **Rector**: PHP automated refactoring
|
||||
- **PHPStan**: Static analysis tool
|
||||
|
||||
### **Testing Framework**
|
||||
- **Pest 3.8.0**: Modern PHP testing framework
|
||||
- **Laravel Dusk**: Browser automation testing
|
||||
- **PHPUnit**: Unit testing foundation
|
||||
|
||||
## External Integrations
|
||||
|
||||
### **Git Providers**
|
||||
- **GitHub**: Repository integration and webhooks
|
||||
- **GitLab**: Self-hosted and cloud GitLab support
|
||||
- **Bitbucket**: Atlassian integration
|
||||
- **Gitea**: Self-hosted Git service
|
||||
|
||||
### **Cloud Storage**
|
||||
- **AWS S3**: [league/flysystem-aws-s3-v3](mdc:composer.json)
|
||||
- **SFTP**: [league/flysystem-sftp-v3](mdc:composer.json)
|
||||
- **Local Storage**: File system integration
|
||||
|
||||
### **Notification Services**
|
||||
- **Email**: [resend/resend-laravel](mdc:composer.json)
|
||||
- **Discord**: Custom webhook integration
|
||||
- **Slack**: Webhook notifications
|
||||
- **Telegram**: Bot API integration
|
||||
- **Pushover**: Push notifications
|
||||
|
||||
### **Monitoring & Logging**
|
||||
- **Sentry**: [sentry/sentry-laravel](mdc:composer.json) - Error tracking
|
||||
- **Laravel Ray**: [spatie/laravel-ray](mdc:composer.json) - Debug tool
|
||||
- **Activity Log**: [spatie/laravel-activitylog](mdc:composer.json)
|
||||
|
||||
## DevOps & Infrastructure
|
||||
|
||||
### **Docker & Containerization**
|
||||
- **Docker**: Container runtime
|
||||
- **Docker Compose**: Multi-container orchestration
|
||||
- **Docker Swarm**: Container clustering (optional)
|
||||
|
||||
### **Web Servers & Proxies**
|
||||
- **Nginx**: Primary web server
|
||||
- **Traefik**: Reverse proxy and load balancer
|
||||
- **Caddy**: Alternative reverse proxy
|
||||
|
||||
### **Process Management**
|
||||
- **S6 Overlay**: Process supervisor
|
||||
- **Supervisor**: Alternative process manager
|
||||
|
||||
### **SSL/TLS**
|
||||
- **Let's Encrypt**: Automatic SSL certificates
|
||||
- **Custom Certificates**: Manual SSL management
|
||||
|
||||
## Terminal & Code Editing
|
||||
|
||||
### **XTerm.js 5.5.0**
|
||||
- **Purpose**: Web-based terminal emulator
|
||||
- **Features**: SSH session management, real-time command execution
|
||||
- **Addons**: Fit addon for responsive terminals
|
||||
|
||||
### **Monaco Editor**
|
||||
- **Purpose**: Code editor component
|
||||
- **Features**: Syntax highlighting, auto-completion
|
||||
- **Integration**: Environment variable editing, configuration files
|
||||
|
||||
## API & Documentation
|
||||
|
||||
### **OpenAPI/Swagger**
|
||||
- **Documentation**: [openapi.json](mdc:openapi.json) (373KB)
|
||||
- **Generator**: [zircote/swagger-php](mdc:composer.json)
|
||||
- **API Routes**: [routes/api.php](mdc:routes/api.php)
|
||||
|
||||
### **WebSocket Communication**
|
||||
- **Laravel Echo**: Real-time event broadcasting
|
||||
- **Pusher**: WebSocket service integration
|
||||
- **Soketi**: Self-hosted WebSocket server
|
||||
|
||||
## Package Management
|
||||
|
||||
### **PHP Dependencies** ([composer.json](mdc:composer.json))
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"php": "^8.4",
|
||||
"laravel/framework": "12.4.1",
|
||||
"livewire/livewire": "^3.5.20",
|
||||
"spatie/laravel-data": "^4.13.1",
|
||||
"lorisleiva/laravel-actions": "^2.8.6"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **JavaScript Dependencies** ([package.json](mdc:package.json))
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
"vite": "^6.2.6",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"@vitejs/plugin-vue": "5.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"ioredis": "5.6.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Files
|
||||
|
||||
### **Build Configuration**
|
||||
- **[vite.config.js](mdc:vite.config.js)**: Frontend build setup
|
||||
- **[postcss.config.cjs](mdc:postcss.config.cjs)**: CSS processing
|
||||
- **[rector.php](mdc:rector.php)**: PHP refactoring rules
|
||||
- **[pint.json](mdc:pint.json)**: Code style configuration
|
||||
|
||||
### **Testing Configuration**
|
||||
- **[phpunit.xml](mdc:phpunit.xml)**: Unit test configuration
|
||||
- **[phpunit.dusk.xml](mdc:phpunit.dusk.xml)**: Browser test configuration
|
||||
- **[tests/Pest.php](mdc:tests/Pest.php)**: Pest testing setup
|
||||
|
||||
## Version Requirements
|
||||
|
||||
### **Minimum Requirements**
|
||||
- **PHP**: 8.4+
|
||||
- **Node.js**: 18+ (for build tools)
|
||||
- **PostgreSQL**: 15+
|
||||
- **Redis**: 7+
|
||||
- **Docker**: 20.10+
|
||||
- **Docker Compose**: 2.0+
|
||||
|
||||
### **Recommended Versions**
|
||||
- **Ubuntu**: 22.04 LTS or 24.04 LTS
|
||||
- **Memory**: 2GB+ RAM
|
||||
- **Storage**: 20GB+ available space
|
||||
- **Network**: Stable internet connection for deployments
|
606
.cursor/rules/testing-patterns.mdc
Normal file
606
.cursor/rules/testing-patterns.mdc
Normal file
@@ -0,0 +1,606 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Coolify Testing Architecture & Patterns
|
||||
|
||||
## Testing Philosophy
|
||||
|
||||
Coolify employs **comprehensive testing strategies** using modern PHP testing frameworks to ensure reliability of deployment operations, infrastructure management, and user interactions.
|
||||
|
||||
## Testing Framework Stack
|
||||
|
||||
### Core Testing Tools
|
||||
- **Pest PHP 3.8+** - Primary testing framework with expressive syntax
|
||||
- **Laravel Dusk** - Browser automation and end-to-end testing
|
||||
- **PHPUnit** - Underlying unit testing framework
|
||||
- **Mockery** - Mocking and stubbing for isolated tests
|
||||
|
||||
### Testing Configuration
|
||||
- **[tests/Pest.php](mdc:tests/Pest.php)** - Pest configuration and global setup (1.5KB, 45 lines)
|
||||
- **[tests/TestCase.php](mdc:tests/TestCase.php)** - Base test case class (163B, 11 lines)
|
||||
- **[tests/CreatesApplication.php](mdc:tests/CreatesApplication.php)** - Application factory trait (375B, 22 lines)
|
||||
- **[tests/DuskTestCase.php](mdc:tests/DuskTestCase.php)** - Browser testing setup (1.4KB, 58 lines)
|
||||
|
||||
## Test Directory Structure
|
||||
|
||||
### Test Organization
|
||||
- **[tests/Feature/](mdc:tests/Feature)** - Feature and integration tests
|
||||
- **[tests/Unit/](mdc:tests/Unit)** - Unit tests for isolated components
|
||||
- **[tests/Browser/](mdc:tests/Browser)** - Laravel Dusk browser tests
|
||||
- **[tests/Traits/](mdc:tests/Traits)** - Shared testing utilities
|
||||
|
||||
## Unit Testing Patterns
|
||||
|
||||
### Model Testing
|
||||
```php
|
||||
// Testing Eloquent models
|
||||
test('application model has correct relationships', function () {
|
||||
$application = Application::factory()->create();
|
||||
|
||||
expect($application->server)->toBeInstanceOf(Server::class);
|
||||
expect($application->environment)->toBeInstanceOf(Environment::class);
|
||||
expect($application->deployments)->toBeInstanceOf(Collection::class);
|
||||
});
|
||||
|
||||
test('application can generate deployment configuration', function () {
|
||||
$application = Application::factory()->create([
|
||||
'name' => 'test-app',
|
||||
'git_repository' => 'https://github.com/user/repo.git'
|
||||
]);
|
||||
|
||||
$config = $application->generateDockerCompose();
|
||||
|
||||
expect($config)->toContain('test-app');
|
||||
expect($config)->toContain('image:');
|
||||
expect($config)->toContain('networks:');
|
||||
});
|
||||
```
|
||||
|
||||
### Service Layer Testing
|
||||
```php
|
||||
// Testing service classes
|
||||
test('configuration generator creates valid docker compose', function () {
|
||||
$generator = new ConfigurationGenerator();
|
||||
$application = Application::factory()->create();
|
||||
|
||||
$compose = $generator->generateDockerCompose($application);
|
||||
|
||||
expect($compose)->toBeString();
|
||||
expect(yaml_parse($compose))->toBeArray();
|
||||
expect($compose)->toContain('version: "3.8"');
|
||||
});
|
||||
|
||||
test('docker image parser validates image names', function () {
|
||||
$parser = new DockerImageParser();
|
||||
|
||||
expect($parser->isValid('nginx:latest'))->toBeTrue();
|
||||
expect($parser->isValid('invalid-image-name'))->toBeFalse();
|
||||
expect($parser->parse('nginx:1.21'))->toEqual([
|
||||
'registry' => 'docker.io',
|
||||
'namespace' => 'library',
|
||||
'repository' => 'nginx',
|
||||
'tag' => '1.21'
|
||||
]);
|
||||
});
|
||||
```
|
||||
|
||||
### Action Testing
|
||||
```php
|
||||
// Testing Laravel Actions
|
||||
test('deploy application action creates deployment queue', function () {
|
||||
$application = Application::factory()->create();
|
||||
$action = new DeployApplicationAction();
|
||||
|
||||
$deployment = $action->handle($application);
|
||||
|
||||
expect($deployment)->toBeInstanceOf(ApplicationDeploymentQueue::class);
|
||||
expect($deployment->status)->toBe('queued');
|
||||
expect($deployment->application_id)->toBe($application->id);
|
||||
});
|
||||
|
||||
test('server validation action checks ssh connectivity', function () {
|
||||
$server = Server::factory()->create([
|
||||
'ip' => '192.168.1.100',
|
||||
'port' => 22
|
||||
]);
|
||||
|
||||
$action = new ValidateServerAction();
|
||||
|
||||
// Mock SSH connection
|
||||
$this->mock(SshConnection::class, function ($mock) {
|
||||
$mock->shouldReceive('connect')->andReturn(true);
|
||||
$mock->shouldReceive('execute')->with('docker --version')->andReturn('Docker version 20.10.0');
|
||||
});
|
||||
|
||||
$result = $action->handle($server);
|
||||
|
||||
expect($result['ssh_connection'])->toBeTrue();
|
||||
expect($result['docker_installed'])->toBeTrue();
|
||||
});
|
||||
```
|
||||
|
||||
## Feature Testing Patterns
|
||||
|
||||
### API Testing
|
||||
```php
|
||||
// Testing API endpoints
|
||||
test('authenticated user can list applications', function () {
|
||||
$user = User::factory()->create();
|
||||
$team = Team::factory()->create();
|
||||
$user->teams()->attach($team);
|
||||
|
||||
$applications = Application::factory(3)->create([
|
||||
'team_id' => $team->id
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->getJson('/api/v1/applications');
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonCount(3, 'data')
|
||||
->assertJsonStructure([
|
||||
'data' => [
|
||||
'*' => ['id', 'name', 'fqdn', 'status', 'created_at']
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
test('user cannot access applications from other teams', function () {
|
||||
$user = User::factory()->create();
|
||||
$otherTeam = Team::factory()->create();
|
||||
|
||||
$application = Application::factory()->create([
|
||||
'team_id' => $otherTeam->id
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->getJson("/api/v1/applications/{$application->id}");
|
||||
|
||||
$response->assertStatus(403);
|
||||
});
|
||||
```
|
||||
|
||||
### Deployment Testing
|
||||
```php
|
||||
// Testing deployment workflows
|
||||
test('application deployment creates docker containers', function () {
|
||||
$application = Application::factory()->create([
|
||||
'git_repository' => 'https://github.com/laravel/laravel.git',
|
||||
'git_branch' => 'main'
|
||||
]);
|
||||
|
||||
// Mock Docker operations
|
||||
$this->mock(DockerService::class, function ($mock) {
|
||||
$mock->shouldReceive('buildImage')->andReturn('app:latest');
|
||||
$mock->shouldReceive('createContainer')->andReturn('container_id');
|
||||
$mock->shouldReceive('startContainer')->andReturn(true);
|
||||
});
|
||||
|
||||
$deployment = $application->deploy();
|
||||
|
||||
expect($deployment->status)->toBe('queued');
|
||||
|
||||
// Process the deployment job
|
||||
$this->artisan('queue:work --once');
|
||||
|
||||
$deployment->refresh();
|
||||
expect($deployment->status)->toBe('success');
|
||||
});
|
||||
|
||||
test('failed deployment triggers rollback', function () {
|
||||
$application = Application::factory()->create();
|
||||
|
||||
// Mock failed deployment
|
||||
$this->mock(DockerService::class, function ($mock) {
|
||||
$mock->shouldReceive('buildImage')->andThrow(new DeploymentException('Build failed'));
|
||||
});
|
||||
|
||||
$deployment = $application->deploy();
|
||||
|
||||
$this->artisan('queue:work --once');
|
||||
|
||||
$deployment->refresh();
|
||||
expect($deployment->status)->toBe('failed');
|
||||
expect($deployment->error_message)->toContain('Build failed');
|
||||
});
|
||||
```
|
||||
|
||||
### Webhook Testing
|
||||
```php
|
||||
// Testing webhook endpoints
|
||||
test('github webhook triggers deployment', function () {
|
||||
$application = Application::factory()->create([
|
||||
'git_repository' => 'https://github.com/user/repo.git',
|
||||
'git_branch' => 'main'
|
||||
]);
|
||||
|
||||
$payload = [
|
||||
'ref' => 'refs/heads/main',
|
||||
'repository' => [
|
||||
'clone_url' => 'https://github.com/user/repo.git'
|
||||
],
|
||||
'head_commit' => [
|
||||
'id' => 'abc123',
|
||||
'message' => 'Update application'
|
||||
]
|
||||
];
|
||||
|
||||
$response = $this->postJson("/webhooks/github/{$application->id}", $payload);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
expect($application->deployments()->count())->toBe(1);
|
||||
expect($application->deployments()->first()->commit_sha)->toBe('abc123');
|
||||
});
|
||||
|
||||
test('webhook validates payload signature', function () {
|
||||
$application = Application::factory()->create();
|
||||
|
||||
$payload = ['invalid' => 'payload'];
|
||||
|
||||
$response = $this->postJson("/webhooks/github/{$application->id}", $payload);
|
||||
|
||||
$response->assertStatus(400);
|
||||
});
|
||||
```
|
||||
|
||||
## Browser Testing (Laravel Dusk)
|
||||
|
||||
### End-to-End Testing
|
||||
```php
|
||||
// Testing complete user workflows
|
||||
test('user can create and deploy application', function () {
|
||||
$user = User::factory()->create();
|
||||
$server = Server::factory()->create(['team_id' => $user->currentTeam->id]);
|
||||
|
||||
$this->browse(function (Browser $browser) use ($user, $server) {
|
||||
$browser->loginAs($user)
|
||||
->visit('/applications/create')
|
||||
->type('name', 'Test Application')
|
||||
->type('git_repository', 'https://github.com/laravel/laravel.git')
|
||||
->type('git_branch', 'main')
|
||||
->select('server_id', $server->id)
|
||||
->press('Create Application')
|
||||
->assertPathIs('/applications/*')
|
||||
->assertSee('Test Application')
|
||||
->press('Deploy')
|
||||
->waitForText('Deployment started', 10)
|
||||
->assertSee('Deployment started');
|
||||
});
|
||||
});
|
||||
|
||||
test('user can monitor deployment logs in real-time', function () {
|
||||
$user = User::factory()->create();
|
||||
$application = Application::factory()->create(['team_id' => $user->currentTeam->id]);
|
||||
|
||||
$this->browse(function (Browser $browser) use ($user, $application) {
|
||||
$browser->loginAs($user)
|
||||
->visit("/applications/{$application->id}")
|
||||
->press('Deploy')
|
||||
->waitForText('Deployment started')
|
||||
->click('@logs-tab')
|
||||
->waitFor('@deployment-logs')
|
||||
->assertSee('Building Docker image')
|
||||
->waitForText('Deployment completed', 30);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### UI Component Testing
|
||||
```php
|
||||
// Testing Livewire components
|
||||
test('server status component updates in real-time', function () {
|
||||
$user = User::factory()->create();
|
||||
$server = Server::factory()->create(['team_id' => $user->currentTeam->id]);
|
||||
|
||||
$this->browse(function (Browser $browser) use ($user, $server) {
|
||||
$browser->loginAs($user)
|
||||
->visit("/servers/{$server->id}")
|
||||
->assertSee('Status: Online')
|
||||
->waitFor('@server-metrics')
|
||||
->assertSee('CPU Usage')
|
||||
->assertSee('Memory Usage')
|
||||
->assertSee('Disk Usage');
|
||||
|
||||
// Simulate server going offline
|
||||
$server->update(['status' => 'offline']);
|
||||
|
||||
$browser->waitForText('Status: Offline', 5)
|
||||
->assertSee('Status: Offline');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Database Testing Patterns
|
||||
|
||||
### Migration Testing
|
||||
```php
|
||||
// Testing database migrations
|
||||
test('applications table has correct structure', function () {
|
||||
expect(Schema::hasTable('applications'))->toBeTrue();
|
||||
expect(Schema::hasColumns('applications', [
|
||||
'id', 'name', 'fqdn', 'git_repository', 'git_branch',
|
||||
'server_id', 'environment_id', 'created_at', 'updated_at'
|
||||
]))->toBeTrue();
|
||||
});
|
||||
|
||||
test('foreign key constraints are properly set', function () {
|
||||
$application = Application::factory()->create();
|
||||
|
||||
expect($application->server)->toBeInstanceOf(Server::class);
|
||||
expect($application->environment)->toBeInstanceOf(Environment::class);
|
||||
|
||||
// Test cascade deletion
|
||||
$application->server->delete();
|
||||
expect(Application::find($application->id))->toBeNull();
|
||||
});
|
||||
```
|
||||
|
||||
### Factory Testing
|
||||
```php
|
||||
// Testing model factories
|
||||
test('application factory creates valid models', function () {
|
||||
$application = Application::factory()->create();
|
||||
|
||||
expect($application->name)->toBeString();
|
||||
expect($application->git_repository)->toStartWith('https://');
|
||||
expect($application->server_id)->toBeInt();
|
||||
expect($application->environment_id)->toBeInt();
|
||||
});
|
||||
|
||||
test('application factory can create with custom attributes', function () {
|
||||
$application = Application::factory()->create([
|
||||
'name' => 'Custom App',
|
||||
'git_branch' => 'develop'
|
||||
]);
|
||||
|
||||
expect($application->name)->toBe('Custom App');
|
||||
expect($application->git_branch)->toBe('develop');
|
||||
});
|
||||
```
|
||||
|
||||
## Queue Testing
|
||||
|
||||
### Job Testing
|
||||
```php
|
||||
// Testing background jobs
|
||||
test('deploy application job processes successfully', function () {
|
||||
$application = Application::factory()->create();
|
||||
$deployment = ApplicationDeploymentQueue::factory()->create([
|
||||
'application_id' => $application->id,
|
||||
'status' => 'queued'
|
||||
]);
|
||||
|
||||
$job = new DeployApplicationJob($deployment);
|
||||
|
||||
// Mock external dependencies
|
||||
$this->mock(DockerService::class, function ($mock) {
|
||||
$mock->shouldReceive('buildImage')->andReturn('app:latest');
|
||||
$mock->shouldReceive('deployContainer')->andReturn(true);
|
||||
});
|
||||
|
||||
$job->handle();
|
||||
|
||||
$deployment->refresh();
|
||||
expect($deployment->status)->toBe('success');
|
||||
});
|
||||
|
||||
test('failed job is retried with exponential backoff', function () {
|
||||
$application = Application::factory()->create();
|
||||
$deployment = ApplicationDeploymentQueue::factory()->create([
|
||||
'application_id' => $application->id
|
||||
]);
|
||||
|
||||
$job = new DeployApplicationJob($deployment);
|
||||
|
||||
// Mock failure
|
||||
$this->mock(DockerService::class, function ($mock) {
|
||||
$mock->shouldReceive('buildImage')->andThrow(new Exception('Network error'));
|
||||
});
|
||||
|
||||
expect(fn() => $job->handle())->toThrow(Exception::class);
|
||||
|
||||
// Job should be retried
|
||||
expect($job->tries)->toBe(3);
|
||||
expect($job->backoff())->toBe([1, 5, 10]);
|
||||
});
|
||||
```
|
||||
|
||||
## Security Testing
|
||||
|
||||
### Authentication Testing
|
||||
```php
|
||||
// Testing authentication and authorization
|
||||
test('unauthenticated users cannot access protected routes', function () {
|
||||
$response = $this->get('/dashboard');
|
||||
$response->assertRedirect('/login');
|
||||
});
|
||||
|
||||
test('users can only access their team resources', function () {
|
||||
$user1 = User::factory()->create();
|
||||
$user2 = User::factory()->create();
|
||||
|
||||
$team1 = Team::factory()->create();
|
||||
$team2 = Team::factory()->create();
|
||||
|
||||
$user1->teams()->attach($team1);
|
||||
$user2->teams()->attach($team2);
|
||||
|
||||
$application = Application::factory()->create(['team_id' => $team1->id]);
|
||||
|
||||
$response = $this->actingAs($user2)
|
||||
->get("/applications/{$application->id}");
|
||||
|
||||
$response->assertStatus(403);
|
||||
});
|
||||
```
|
||||
|
||||
### Input Validation Testing
|
||||
```php
|
||||
// Testing input validation and sanitization
|
||||
test('application creation validates required fields', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->postJson('/api/v1/applications', []);
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonValidationErrors(['name', 'git_repository', 'server_id']);
|
||||
});
|
||||
|
||||
test('malicious input is properly sanitized', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->postJson('/api/v1/applications', [
|
||||
'name' => '<script>alert("xss")</script>',
|
||||
'git_repository' => 'javascript:alert("xss")',
|
||||
'server_id' => 'invalid'
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
});
|
||||
```
|
||||
|
||||
## Performance Testing
|
||||
|
||||
### Load Testing
|
||||
```php
|
||||
// Testing application performance under load
|
||||
test('application list endpoint handles concurrent requests', function () {
|
||||
$user = User::factory()->create();
|
||||
$applications = Application::factory(100)->create(['team_id' => $user->currentTeam->id]);
|
||||
|
||||
$startTime = microtime(true);
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->getJson('/api/v1/applications');
|
||||
|
||||
$endTime = microtime(true);
|
||||
$responseTime = ($endTime - $startTime) * 1000; // Convert to milliseconds
|
||||
|
||||
$response->assertStatus(200);
|
||||
expect($responseTime)->toBeLessThan(500); // Should respond within 500ms
|
||||
});
|
||||
```
|
||||
|
||||
### Memory Usage Testing
|
||||
```php
|
||||
// Testing memory efficiency
|
||||
test('deployment process does not exceed memory limits', function () {
|
||||
$initialMemory = memory_get_usage();
|
||||
|
||||
$application = Application::factory()->create();
|
||||
$deployment = $application->deploy();
|
||||
|
||||
// Process deployment
|
||||
$this->artisan('queue:work --once');
|
||||
|
||||
$finalMemory = memory_get_usage();
|
||||
$memoryIncrease = $finalMemory - $initialMemory;
|
||||
|
||||
expect($memoryIncrease)->toBeLessThan(50 * 1024 * 1024); // Less than 50MB
|
||||
});
|
||||
```
|
||||
|
||||
## Test Utilities and Helpers
|
||||
|
||||
### Custom Assertions
|
||||
```php
|
||||
// Custom test assertions
|
||||
expect()->extend('toBeValidDockerCompose', function () {
|
||||
$yaml = yaml_parse($this->value);
|
||||
|
||||
return $yaml !== false &&
|
||||
isset($yaml['version']) &&
|
||||
isset($yaml['services']) &&
|
||||
is_array($yaml['services']);
|
||||
});
|
||||
|
||||
expect()->extend('toHaveValidSshConnection', function () {
|
||||
$server = $this->value;
|
||||
|
||||
try {
|
||||
$connection = new SshConnection($server);
|
||||
return $connection->test();
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Test Traits
|
||||
```php
|
||||
// Shared testing functionality
|
||||
trait CreatesTestServers
|
||||
{
|
||||
protected function createTestServer(array $attributes = []): Server
|
||||
{
|
||||
return Server::factory()->create(array_merge([
|
||||
'name' => 'Test Server',
|
||||
'ip' => '127.0.0.1',
|
||||
'port' => 22,
|
||||
'team_id' => $this->user->currentTeam->id
|
||||
], $attributes));
|
||||
}
|
||||
}
|
||||
|
||||
trait MocksDockerOperations
|
||||
{
|
||||
protected function mockDockerService(): void
|
||||
{
|
||||
$this->mock(DockerService::class, function ($mock) {
|
||||
$mock->shouldReceive('buildImage')->andReturn('test:latest');
|
||||
$mock->shouldReceive('createContainer')->andReturn('container_123');
|
||||
$mock->shouldReceive('startContainer')->andReturn(true);
|
||||
$mock->shouldReceive('stopContainer')->andReturn(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Continuous Integration Testing
|
||||
|
||||
### GitHub Actions Integration
|
||||
```yaml
|
||||
# .github/workflows/tests.yml
|
||||
name: Tests
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
env:
|
||||
POSTGRES_PASSWORD: password
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.4
|
||||
- name: Install dependencies
|
||||
run: composer install
|
||||
- name: Run tests
|
||||
run: ./vendor/bin/pest
|
||||
```
|
||||
|
||||
### Test Coverage
|
||||
```php
|
||||
// Generate test coverage reports
|
||||
test('application has adequate test coverage', function () {
|
||||
$coverage = $this->getCoverageData();
|
||||
|
||||
expect($coverage['application'])->toBeGreaterThan(80);
|
||||
expect($coverage['models'])->toBeGreaterThan(90);
|
||||
expect($coverage['actions'])->toBeGreaterThan(85);
|
||||
});
|
||||
```
|
Reference in New Issue
Block a user