feat(docs): update architecture and development guidelines; enhance form components with built-in authorization system and improve routing documentation
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description:
|
description: Complete guide to Coolify Cursor rules and development patterns
|
||||||
globs:
|
globs: .cursor/rules/*.mdc
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
# Coolify Cursor Rules - Complete Guide
|
# Coolify Cursor Rules - Complete Guide
|
||||||
@@ -18,6 +18,7 @@ This comprehensive set of Cursor Rules provides deep insights into **Coolify**,
|
|||||||
|
|
||||||
### 🎨 Frontend Development
|
### 🎨 Frontend Development
|
||||||
- **[frontend-patterns.mdc](mdc:.cursor/rules/frontend-patterns.mdc)** - Livewire + Alpine.js + Tailwind architecture
|
- **[frontend-patterns.mdc](mdc:.cursor/rules/frontend-patterns.mdc)** - Livewire + Alpine.js + Tailwind architecture
|
||||||
|
- **[form-components.mdc](mdc:.cursor/rules/form-components.mdc)** - Enhanced form components with built-in authorization
|
||||||
|
|
||||||
### 🗄️ Data & Backend
|
### 🗄️ Data & Backend
|
||||||
- **[database-patterns.mdc](mdc:.cursor/rules/database-patterns.mdc)** - Database architecture, models, and data management
|
- **[database-patterns.mdc](mdc:.cursor/rules/database-patterns.mdc)** - Database architecture, models, and data management
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description:
|
description: RESTful API design, routing patterns, webhooks, and HTTP communication
|
||||||
globs:
|
globs: routes/*.php, app/Http/Controllers/**/*.php, app/Http/Resources/*.php, app/Http/Requests/*.php
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
# Coolify API & Routing Architecture
|
# Coolify API & Routing Architecture
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description:
|
description: Laravel application structure, patterns, and architectural decisions
|
||||||
globs:
|
globs: app/**/*.php, config/*.php, bootstrap/**/*.php
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
# Coolify Application Architecture
|
# Coolify Application Architecture
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description:
|
description: Database architecture, models, migrations, relationships, and data management patterns
|
||||||
globs:
|
globs: app/Models/*.php, database/migrations/*.php, database/seeders/*.php, app/Actions/Database/*.php
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
# Coolify Database Architecture & Patterns
|
# Coolify Database Architecture & Patterns
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description:
|
description: Docker orchestration, deployment workflows, and containerization patterns
|
||||||
globs:
|
globs: app/Jobs/*.php, app/Actions/Application/*.php, app/Actions/Server/*.php, docker/*.*, *.yml, *.yaml
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
# Coolify Deployment Architecture
|
# Coolify Deployment Architecture
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description:
|
description: Development setup, coding standards, contribution guidelines, and best practices
|
||||||
globs:
|
globs: **/*.php, composer.json, package.json, *.md, .env.example
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
# Coolify Development Workflow
|
# Coolify Development Workflow
|
||||||
|
|||||||
431
.cursor/rules/form-components.mdc
Normal file
431
.cursor/rules/form-components.mdc
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
---
|
||||||
|
description: Enhanced form components with built-in authorization system
|
||||||
|
globs: resources/views/**/*.blade.php, app/View/Components/Forms/*.php
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Enhanced Form Components with Authorization
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Coolify's form components now feature **built-in authorization** that automatically handles permission-based UI control, dramatically reducing code duplication and improving security consistency.
|
||||||
|
|
||||||
|
## Enhanced Components
|
||||||
|
|
||||||
|
All form components now support the `canGate` authorization system:
|
||||||
|
|
||||||
|
- **[Input.php](mdc:app/View/Components/Forms/Input.php)** - Text, password, and other input fields
|
||||||
|
- **[Select.php](mdc:app/View/Components/Forms/Select.php)** - Dropdown selection components
|
||||||
|
- **[Textarea.php](mdc:app/View/Components/Forms/Textarea.php)** - Multi-line text areas
|
||||||
|
- **[Checkbox.php](mdc:app/View/Components/Forms/Checkbox.php)** - Boolean toggle components
|
||||||
|
- **[Button.php](mdc:app/View/Components/Forms/Button.php)** - Action buttons
|
||||||
|
|
||||||
|
## Authorization Parameters
|
||||||
|
|
||||||
|
### Core Parameters
|
||||||
|
```php
|
||||||
|
public ?string $canGate = null; // Gate name: 'update', 'view', 'deploy', 'delete'
|
||||||
|
public mixed $canResource = null; // Resource model instance to check against
|
||||||
|
public bool $autoDisable = true; // Automatically disable if no permission
|
||||||
|
```
|
||||||
|
|
||||||
|
### How It Works
|
||||||
|
```php
|
||||||
|
// Automatic authorization logic in each component
|
||||||
|
if ($this->canGate && $this->canResource && $this->autoDisable) {
|
||||||
|
$hasPermission = Gate::allows($this->canGate, $this->canResource);
|
||||||
|
|
||||||
|
if (! $hasPermission) {
|
||||||
|
$this->disabled = true;
|
||||||
|
// For Checkbox: also sets $this->instantSave = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Patterns
|
||||||
|
|
||||||
|
### ✅ Recommended: Single Line Pattern
|
||||||
|
|
||||||
|
**Before (Verbose, 6+ lines per element):**
|
||||||
|
```html
|
||||||
|
@can('update', $application)
|
||||||
|
<x-forms.input id="application.name" label="Name" />
|
||||||
|
<x-forms.checkbox instantSave id="application.settings.is_static" label="Static Site" />
|
||||||
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
|
@else
|
||||||
|
<x-forms.input disabled id="application.name" label="Name" />
|
||||||
|
<x-forms.checkbox disabled id="application.settings.is_static" label="Static Site" />
|
||||||
|
@endcan
|
||||||
|
```
|
||||||
|
|
||||||
|
**After (Clean, 1 line per element):**
|
||||||
|
```html
|
||||||
|
<x-forms.input canGate="update" :canResource="$application" id="application.name" label="Name" />
|
||||||
|
<x-forms.checkbox instantSave canGate="update" :canResource="$application" id="application.settings.is_static" label="Static Site" />
|
||||||
|
<x-forms.button canGate="update" :canResource="$application" type="submit">Save</x-forms.button>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Result: 90% code reduction!**
|
||||||
|
|
||||||
|
### Component-Specific Examples
|
||||||
|
|
||||||
|
#### Input Fields
|
||||||
|
```html
|
||||||
|
<!-- Basic input with authorization -->
|
||||||
|
<x-forms.input
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
id="application.name"
|
||||||
|
label="Application Name" />
|
||||||
|
|
||||||
|
<!-- Password input with authorization -->
|
||||||
|
<x-forms.input
|
||||||
|
type="password"
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
id="application.database_password"
|
||||||
|
label="Database Password" />
|
||||||
|
|
||||||
|
<!-- Required input with authorization -->
|
||||||
|
<x-forms.input
|
||||||
|
required
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
id="application.fqdn"
|
||||||
|
label="Domain" />
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Select Dropdowns
|
||||||
|
```html
|
||||||
|
<!-- Build pack selection -->
|
||||||
|
<x-forms.select
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
id="application.build_pack"
|
||||||
|
label="Build Pack"
|
||||||
|
required>
|
||||||
|
<option value="nixpacks">Nixpacks</option>
|
||||||
|
<option value="static">Static</option>
|
||||||
|
<option value="dockerfile">Dockerfile</option>
|
||||||
|
</x-forms.select>
|
||||||
|
|
||||||
|
<!-- Server selection -->
|
||||||
|
<x-forms.select
|
||||||
|
canGate="createAnyResource"
|
||||||
|
:canResource="auth()->user()->currentTeam"
|
||||||
|
id="server_id"
|
||||||
|
label="Target Server">
|
||||||
|
@foreach($servers as $server)
|
||||||
|
<option value="{{ $server->id }}">{{ $server->name }}</option>
|
||||||
|
@endforeach
|
||||||
|
</x-forms.select>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Checkboxes with InstantSave
|
||||||
|
```html
|
||||||
|
<!-- Static site toggle -->
|
||||||
|
<x-forms.checkbox
|
||||||
|
instantSave
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
id="application.settings.is_static"
|
||||||
|
label="Is it a static site?"
|
||||||
|
helper="Enable if your application serves static files" />
|
||||||
|
|
||||||
|
<!-- Debug mode toggle -->
|
||||||
|
<x-forms.checkbox
|
||||||
|
instantSave
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
id="application.settings.is_debug_enabled"
|
||||||
|
label="Debug Mode"
|
||||||
|
helper="Enable debug logging for troubleshooting" />
|
||||||
|
|
||||||
|
<!-- Build server toggle -->
|
||||||
|
<x-forms.checkbox
|
||||||
|
instantSave
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
id="application.settings.is_build_server_enabled"
|
||||||
|
label="Use Build Server"
|
||||||
|
helper="Use a dedicated build server for compilation" />
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Textareas
|
||||||
|
```html
|
||||||
|
<!-- Configuration textarea -->
|
||||||
|
<x-forms.textarea
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
id="application.docker_compose_raw"
|
||||||
|
label="Docker Compose Configuration"
|
||||||
|
rows="10"
|
||||||
|
monacoEditorLanguage="yaml"
|
||||||
|
useMonacoEditor />
|
||||||
|
|
||||||
|
<!-- Custom commands -->
|
||||||
|
<x-forms.textarea
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
id="application.post_deployment_command"
|
||||||
|
label="Post-Deployment Commands"
|
||||||
|
placeholder="php artisan migrate"
|
||||||
|
helper="Commands to run after deployment" />
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Buttons
|
||||||
|
```html
|
||||||
|
<!-- Save button -->
|
||||||
|
<x-forms.button
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
type="submit">
|
||||||
|
Save Configuration
|
||||||
|
</x-forms.button>
|
||||||
|
|
||||||
|
<!-- Deploy button -->
|
||||||
|
<x-forms.button
|
||||||
|
canGate="deploy"
|
||||||
|
:canResource="$application"
|
||||||
|
wire:click="deploy">
|
||||||
|
Deploy Application
|
||||||
|
</x-forms.button>
|
||||||
|
|
||||||
|
<!-- Delete button -->
|
||||||
|
<x-forms.button
|
||||||
|
canGate="delete"
|
||||||
|
:canResource="$application"
|
||||||
|
wire:click="confirmDelete"
|
||||||
|
class="button-danger">
|
||||||
|
Delete Application
|
||||||
|
</x-forms.button>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### Custom Authorization Logic
|
||||||
|
```html
|
||||||
|
<!-- Disable auto-control for complex permissions -->
|
||||||
|
<x-forms.input
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
autoDisable="false"
|
||||||
|
:disabled="$application->is_deployed || !$application->canModifySettings()"
|
||||||
|
id="deployment.setting"
|
||||||
|
label="Advanced Setting" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple Permission Checks
|
||||||
|
```html
|
||||||
|
<!-- Combine multiple authorization requirements -->
|
||||||
|
<x-forms.checkbox
|
||||||
|
canGate="deploy"
|
||||||
|
:canResource="$application"
|
||||||
|
autoDisable="false"
|
||||||
|
:disabled="!$application->hasDockerfile() || !Gate::allows('deploy', $application)"
|
||||||
|
id="docker.setting"
|
||||||
|
label="Docker-Specific Setting" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conditional Resources
|
||||||
|
```html
|
||||||
|
<!-- Different resources based on context -->
|
||||||
|
<x-forms.button
|
||||||
|
:canGate="$isEditing ? 'update' : 'view'"
|
||||||
|
:canResource="$resource"
|
||||||
|
type="submit">
|
||||||
|
{{ $isEditing ? 'Save Changes' : 'View Details' }}
|
||||||
|
</x-forms.button>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supported Gates
|
||||||
|
|
||||||
|
### Resource-Level Gates
|
||||||
|
- `view` - Read access to resource details
|
||||||
|
- `update` - Modify resource configuration and settings
|
||||||
|
- `deploy` - Deploy, restart, or manage resource state
|
||||||
|
- `delete` - Remove or destroy resource
|
||||||
|
- `clone` - Duplicate resource to another location
|
||||||
|
|
||||||
|
### Global Gates
|
||||||
|
- `createAnyResource` - Create new resources of any type
|
||||||
|
- `manageTeam` - Team administration permissions
|
||||||
|
- `accessServer` - Server-level access permissions
|
||||||
|
|
||||||
|
## Supported Resources
|
||||||
|
|
||||||
|
### Primary Resources
|
||||||
|
- `$application` - Application instances and configurations
|
||||||
|
- `$service` - Docker Compose services and components
|
||||||
|
- `$database` - Database instances (PostgreSQL, MySQL, etc.)
|
||||||
|
- `$server` - Physical or virtual server instances
|
||||||
|
|
||||||
|
### Container Resources
|
||||||
|
- `$project` - Project containers and environments
|
||||||
|
- `$environment` - Environment-specific configurations
|
||||||
|
- `$team` - Team and organization contexts
|
||||||
|
|
||||||
|
### Infrastructure Resources
|
||||||
|
- `$privateKey` - SSH private keys and certificates
|
||||||
|
- `$source` - Git sources and repositories
|
||||||
|
- `$destination` - Deployment destinations and targets
|
||||||
|
|
||||||
|
## Component Behavior
|
||||||
|
|
||||||
|
### Input Components (Input, Select, Textarea)
|
||||||
|
When authorization fails:
|
||||||
|
- **disabled = true** - Field becomes non-editable
|
||||||
|
- **Visual styling** - Opacity reduction and disabled cursor
|
||||||
|
- **Form submission** - Values are ignored in forms
|
||||||
|
- **User feedback** - Clear visual indication of restricted access
|
||||||
|
|
||||||
|
### Checkbox Components
|
||||||
|
When authorization fails:
|
||||||
|
- **disabled = true** - Checkbox becomes non-clickable
|
||||||
|
- **instantSave = false** - Automatic saving is disabled
|
||||||
|
- **State preservation** - Current value is maintained but read-only
|
||||||
|
- **Visual styling** - Disabled appearance with reduced opacity
|
||||||
|
|
||||||
|
### Button Components
|
||||||
|
When authorization fails:
|
||||||
|
- **disabled = true** - Button becomes non-clickable
|
||||||
|
- **Event blocking** - Click handlers are ignored
|
||||||
|
- **Visual styling** - Disabled appearance and cursor
|
||||||
|
- **Loading states** - Loading indicators are disabled
|
||||||
|
|
||||||
|
## Migration Guide
|
||||||
|
|
||||||
|
### Converting Existing Forms
|
||||||
|
|
||||||
|
**Old Pattern:**
|
||||||
|
```html
|
||||||
|
<form wire:submit='submit'>
|
||||||
|
@can('update', $application)
|
||||||
|
<x-forms.input id="name" label="Name" />
|
||||||
|
<x-forms.select id="type" label="Type">...</x-forms.select>
|
||||||
|
<x-forms.checkbox instantSave id="enabled" label="Enabled" />
|
||||||
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
|
@else
|
||||||
|
<x-forms.input disabled id="name" label="Name" />
|
||||||
|
<x-forms.select disabled id="type" label="Type">...</x-forms.select>
|
||||||
|
<x-forms.checkbox disabled id="enabled" label="Enabled" />
|
||||||
|
@endcan
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
**New Pattern:**
|
||||||
|
```html
|
||||||
|
<form wire:submit='submit'>
|
||||||
|
<x-forms.input canGate="update" :canResource="$application" id="name" label="Name" />
|
||||||
|
<x-forms.select canGate="update" :canResource="$application" id="type" label="Type">...</x-forms.select>
|
||||||
|
<x-forms.checkbox instantSave canGate="update" :canResource="$application" id="enabled" label="Enabled" />
|
||||||
|
<x-forms.button canGate="update" :canResource="$application" type="submit">Save</x-forms.button>
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gradual Migration Strategy
|
||||||
|
|
||||||
|
1. **Start with new forms** - Use the new pattern for all new components
|
||||||
|
2. **Convert high-traffic areas** - Migrate frequently used forms first
|
||||||
|
3. **Batch convert similar forms** - Group similar authorization patterns
|
||||||
|
4. **Test thoroughly** - Verify authorization behavior matches expectations
|
||||||
|
5. **Remove old patterns** - Clean up legacy @can/@else blocks
|
||||||
|
|
||||||
|
## Testing Patterns
|
||||||
|
|
||||||
|
### Component Authorization Tests
|
||||||
|
```php
|
||||||
|
// Test authorization integration in components
|
||||||
|
test('input component respects authorization', function () {
|
||||||
|
$user = User::factory()->member()->create();
|
||||||
|
$application = Application::factory()->create();
|
||||||
|
|
||||||
|
// Member should see disabled input
|
||||||
|
$component = Livewire::actingAs($user)
|
||||||
|
->test(TestComponent::class, [
|
||||||
|
'canGate' => 'update',
|
||||||
|
'canResource' => $application
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($component->get('disabled'))->toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('checkbox disables instantSave for unauthorized users', function () {
|
||||||
|
$user = User::factory()->member()->create();
|
||||||
|
$application = Application::factory()->create();
|
||||||
|
|
||||||
|
$component = Livewire::actingAs($user)
|
||||||
|
->test(CheckboxComponent::class, [
|
||||||
|
'instantSave' => true,
|
||||||
|
'canGate' => 'update',
|
||||||
|
'canResource' => $application
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($component->get('disabled'))->toBeTrue();
|
||||||
|
expect($component->get('instantSave'))->toBeFalse();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
```php
|
||||||
|
// Test full form authorization behavior
|
||||||
|
test('application form respects member permissions', function () {
|
||||||
|
$member = User::factory()->member()->create();
|
||||||
|
$application = Application::factory()->create();
|
||||||
|
|
||||||
|
$this->actingAs($member)
|
||||||
|
->get(route('application.edit', $application))
|
||||||
|
->assertSee('disabled')
|
||||||
|
->assertDontSee('Save Configuration');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Consistent Gate Usage
|
||||||
|
- Use `update` for configuration changes
|
||||||
|
- Use `deploy` for operational actions
|
||||||
|
- Use `view` for read-only access
|
||||||
|
- Use `delete` for destructive actions
|
||||||
|
|
||||||
|
### Resource Context
|
||||||
|
- Always pass the specific resource being acted upon
|
||||||
|
- Use team context for creation permissions
|
||||||
|
- Consider nested resource relationships
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
- Provide clear feedback for disabled components
|
||||||
|
- Use helper text to explain permission requirements
|
||||||
|
- Consider tooltips for disabled buttons
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- Authorization checks are cached per request
|
||||||
|
- Use eager loading for resource relationships
|
||||||
|
- Consider query optimization for complex permissions
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Application Configuration Forms
|
||||||
|
```html
|
||||||
|
<!-- Application settings with consistent authorization -->
|
||||||
|
<x-forms.input canGate="update" :canResource="$application" id="application.name" label="Name" />
|
||||||
|
<x-forms.select canGate="update" :canResource="$application" id="application.build_pack" label="Build Pack">...</x-forms.select>
|
||||||
|
<x-forms.checkbox instantSave canGate="update" :canResource="$application" id="application.settings.is_static" label="Static Site" />
|
||||||
|
<x-forms.button canGate="update" :canResource="$application" type="submit">Save</x-forms.button>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Server Management Forms
|
||||||
|
```html
|
||||||
|
<!-- Server configuration with appropriate gates -->
|
||||||
|
<x-forms.input canGate="update" :canResource="$server" id="server.name" label="Server Name" />
|
||||||
|
<x-forms.select canGate="update" :canResource="$server" id="server.type" label="Server Type">...</x-forms.select>
|
||||||
|
<x-forms.button canGate="delete" :canResource="$server" wire:click="deleteServer">Delete Server</x-forms.button>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resource Creation Forms
|
||||||
|
```html
|
||||||
|
<!-- New resource creation -->
|
||||||
|
<x-forms.input canGate="createAnyResource" :canResource="auth()->user()->currentTeam" id="name" label="Name" />
|
||||||
|
<x-forms.select canGate="createAnyResource" :canResource="auth()->user()->currentTeam" id="server_id" label="Server">...</x-forms.select>
|
||||||
|
<x-forms.button canGate="createAnyResource" :canResource="auth()->user()->currentTeam" type="submit">Create Application</x-forms.button>
|
||||||
|
```
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description:
|
description: Livewire components, Alpine.js patterns, Tailwind CSS, and enhanced form components
|
||||||
globs:
|
globs: app/Livewire/**/*.php, resources/views/**/*.blade.php, resources/js/**/*.js, resources/css/**/*.css
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
# Coolify Frontend Architecture & Patterns
|
# Coolify Frontend Architecture & Patterns
|
||||||
@@ -230,6 +230,41 @@ class ServerList extends Component
|
|||||||
- **Asset bundling** and compression
|
- **Asset bundling** and compression
|
||||||
- **CDN integration** for static assets
|
- **CDN integration** for static assets
|
||||||
|
|
||||||
|
## Enhanced Form Components
|
||||||
|
|
||||||
|
### Built-in Authorization System
|
||||||
|
Coolify features **enhanced form components** with automatic authorization handling:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- ✅ New Pattern: Single line with built-in authorization -->
|
||||||
|
<x-forms.input canGate="update" :canResource="$application" id="application.name" label="Name" />
|
||||||
|
<x-forms.checkbox instantSave canGate="update" :canResource="$application" id="application.settings.is_static" label="Static Site" />
|
||||||
|
<x-forms.button canGate="update" :canResource="$application" type="submit">Save</x-forms.button>
|
||||||
|
|
||||||
|
<!-- ❌ Old Pattern: Verbose @can/@else blocks (deprecated) -->
|
||||||
|
@can('update', $application)
|
||||||
|
<x-forms.input id="application.name" label="Name" />
|
||||||
|
@else
|
||||||
|
<x-forms.input disabled id="application.name" label="Name" />
|
||||||
|
@endcan
|
||||||
|
```
|
||||||
|
|
||||||
|
### Authorization Parameters
|
||||||
|
```php
|
||||||
|
// Available on all form components (Input, Select, Textarea, Checkbox, Button)
|
||||||
|
public ?string $canGate = null; // Gate name: 'update', 'view', 'deploy', 'delete'
|
||||||
|
public mixed $canResource = null; // Resource model instance to check against
|
||||||
|
public bool $autoDisable = true; // Automatically disable if no permission (default: true)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Benefits
|
||||||
|
- **90% code reduction** for authorization-protected forms
|
||||||
|
- **Consistent security** across all form components
|
||||||
|
- **Automatic disabling** for unauthorized users
|
||||||
|
- **Smart behavior** (disables instantSave on checkboxes for unauthorized users)
|
||||||
|
|
||||||
|
For complete documentation, see **[form-components.mdc](mdc:.cursor/rules/form-components.mdc)**
|
||||||
|
|
||||||
## Form Handling Patterns
|
## Form Handling Patterns
|
||||||
|
|
||||||
### Livewire Forms
|
### Livewire Forms
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description:
|
description: High-level project mission, core concepts, and architectural overview
|
||||||
globs:
|
globs: README.md, CONTRIBUTING.md, CHANGELOG.md, *.md
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
# Coolify Project Overview
|
# Coolify Project Overview
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
description:
|
description: Security architecture, authentication, authorization patterns, and enhanced form component security
|
||||||
globs:
|
globs: app/Policies/*.php, app/View/Components/Forms/*.php, app/Http/Middleware/*.php, resources/views/**/*.blade.php
|
||||||
alwaysApply: false
|
alwaysApply: true
|
||||||
---
|
---
|
||||||
# Coolify Security Architecture & Patterns
|
# Coolify Security Architecture & Patterns
|
||||||
|
|
||||||
@@ -63,6 +63,170 @@ class User extends Authenticatable
|
|||||||
|
|
||||||
## Authorization & Access Control
|
## Authorization & Access Control
|
||||||
|
|
||||||
|
### Enhanced Form Component Authorization System
|
||||||
|
|
||||||
|
Coolify now features a **centralized authorization system** built into all form components (`Input`, `Select`, `Textarea`, `Checkbox`, `Button`) that automatically handles permission-based UI control.
|
||||||
|
|
||||||
|
#### Component Authorization Parameters
|
||||||
|
```php
|
||||||
|
// Available on all form components
|
||||||
|
public ?string $canGate = null; // Gate name (e.g., 'update', 'view', 'delete')
|
||||||
|
public mixed $canResource = null; // Resource to check against (model instance)
|
||||||
|
public bool $autoDisable = true; // Auto-disable if no permission (default: true)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Smart Authorization Logic
|
||||||
|
```php
|
||||||
|
// Automatic authorization handling in component constructor
|
||||||
|
if ($this->canGate && $this->canResource && $this->autoDisable) {
|
||||||
|
$hasPermission = Gate::allows($this->canGate, $this->canResource);
|
||||||
|
|
||||||
|
if (! $hasPermission) {
|
||||||
|
$this->disabled = true;
|
||||||
|
// For Checkbox: also disables instantSave
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Usage Examples
|
||||||
|
|
||||||
|
**✅ Recommended Pattern (Single Line):**
|
||||||
|
```html
|
||||||
|
<!-- Input with automatic authorization -->
|
||||||
|
<x-forms.input
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
id="application.name"
|
||||||
|
label="Application Name" />
|
||||||
|
|
||||||
|
<!-- Select with automatic authorization -->
|
||||||
|
<x-forms.select
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
id="application.build_pack"
|
||||||
|
label="Build Pack">
|
||||||
|
<option value="nixpacks">Nixpacks</option>
|
||||||
|
<option value="static">Static</option>
|
||||||
|
</x-forms.select>
|
||||||
|
|
||||||
|
<!-- Checkbox with automatic instantSave control -->
|
||||||
|
<x-forms.checkbox
|
||||||
|
instantSave
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
id="application.settings.is_static"
|
||||||
|
label="Is Static Site?" />
|
||||||
|
|
||||||
|
<!-- Button with automatic disable -->
|
||||||
|
<x-forms.button
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
type="submit">
|
||||||
|
Save Configuration
|
||||||
|
</x-forms.button>
|
||||||
|
```
|
||||||
|
|
||||||
|
**❌ Old Pattern (Verbose, Deprecated):**
|
||||||
|
```html
|
||||||
|
<!-- DON'T use this repetitive pattern anymore -->
|
||||||
|
@can('update', $application)
|
||||||
|
<x-forms.input id="application.name" label="Application Name" />
|
||||||
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
|
@else
|
||||||
|
<x-forms.input disabled id="application.name" label="Application Name" />
|
||||||
|
@endcan
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Advanced Usage with Custom Control
|
||||||
|
|
||||||
|
**Custom Authorization Logic:**
|
||||||
|
```html
|
||||||
|
<!-- Disable auto-control, use custom logic -->
|
||||||
|
<x-forms.input
|
||||||
|
canGate="update"
|
||||||
|
:canResource="$application"
|
||||||
|
autoDisable="false"
|
||||||
|
:disabled="$application->is_deployed || !Gate::allows('update', $application)"
|
||||||
|
id="advanced.setting"
|
||||||
|
label="Advanced Setting" />
|
||||||
|
```
|
||||||
|
|
||||||
|
**Multiple Permission Checks:**
|
||||||
|
```html
|
||||||
|
<!-- Complex permission requirements -->
|
||||||
|
<x-forms.checkbox
|
||||||
|
canGate="deploy"
|
||||||
|
:canResource="$application"
|
||||||
|
autoDisable="false"
|
||||||
|
:disabled="!$application->canDeploy() || !auth()->user()->hasAdvancedPermissions()"
|
||||||
|
id="deployment.setting"
|
||||||
|
label="Advanced Deployment Setting" />
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Supported Gates and Resources
|
||||||
|
|
||||||
|
**Common Gates:**
|
||||||
|
- `view` - Read access to resource
|
||||||
|
- `update` - Modify resource configuration
|
||||||
|
- `deploy` - Deploy/restart resource
|
||||||
|
- `delete` - Remove resource
|
||||||
|
- `createAnyResource` - Create new resources
|
||||||
|
|
||||||
|
**Resource Types:**
|
||||||
|
- `Application` - Application instances
|
||||||
|
- `Service` - Docker Compose services
|
||||||
|
- `Server` - Server instances
|
||||||
|
- `Project` - Project containers
|
||||||
|
- `Environment` - Environment contexts
|
||||||
|
- `Database` - Database instances
|
||||||
|
|
||||||
|
#### Benefits
|
||||||
|
|
||||||
|
**🔥 Massive Code Reduction:**
|
||||||
|
- **90% less code** for authorization-protected forms
|
||||||
|
- **Single line** instead of 6-12 lines per form element
|
||||||
|
- **No more @can/@else blocks** cluttering templates
|
||||||
|
|
||||||
|
**🛡️ Consistent Security:**
|
||||||
|
- **Unified authorization logic** across all form components
|
||||||
|
- **Automatic disabling** for unauthorized users
|
||||||
|
- **Smart behavior** (like disabling instantSave on checkboxes)
|
||||||
|
|
||||||
|
**🎨 Better UX:**
|
||||||
|
- **Consistent disabled styling** across all components
|
||||||
|
- **Proper visual feedback** for restricted access
|
||||||
|
- **Clean, professional interface**
|
||||||
|
|
||||||
|
#### Implementation Details
|
||||||
|
|
||||||
|
**Component Enhancement:**
|
||||||
|
```php
|
||||||
|
// Enhanced in all form components
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
// ... existing parameters
|
||||||
|
public ?string $canGate = null,
|
||||||
|
public mixed $canResource = null,
|
||||||
|
public bool $autoDisable = true,
|
||||||
|
) {
|
||||||
|
// Handle authorization-based disabling
|
||||||
|
if ($this->canGate && $this->canResource && $this->autoDisable) {
|
||||||
|
$hasPermission = Gate::allows($this->canGate, $this->canResource);
|
||||||
|
|
||||||
|
if (! $hasPermission) {
|
||||||
|
$this->disabled = true;
|
||||||
|
// For Checkbox: $this->instantSave = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Backward Compatibility:**
|
||||||
|
- All existing form components continue to work unchanged
|
||||||
|
- New authorization parameters are optional
|
||||||
|
- Legacy @can/@else patterns still function but are discouraged
|
||||||
|
|
||||||
### Team-Based Multi-Tenancy
|
### Team-Based Multi-Tenancy
|
||||||
- **[Team.php](mdc:app/Models/Team.php)** - Multi-tenant organization structure (8.9KB, 308 lines)
|
- **[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
|
- **[TeamInvitation.php](mdc:app/Models/TeamInvitation.php)** - Secure team collaboration
|
||||||
|
|||||||
@@ -31,19 +31,6 @@ alwaysApply: true
|
|||||||
- Related rules have been updated
|
- Related rules have been updated
|
||||||
- Implementation details have changed
|
- 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:**
|
- **Rule Quality Checks:**
|
||||||
- Rules should be actionable and specific
|
- Rules should be actionable and specific
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description:
|
description: Complete technology stack, dependencies, and infrastructure components
|
||||||
globs:
|
globs: composer.json, package.json, docker-compose*.yml, config/*.php
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
# Coolify Technology Stack
|
# Coolify Technology Stack
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description:
|
description: Testing strategies with Pest PHP, Laravel Dusk, and quality assurance patterns
|
||||||
globs:
|
globs: tests/**/*.php, database/factories/*.php
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
# Coolify Testing Architecture & Patterns
|
# Coolify Testing Architecture & Patterns
|
||||||
|
|||||||
157
CLAUDE.md
157
CLAUDE.md
@@ -29,21 +29,22 @@ Only run artisan commands inside "coolify" container when in development.
|
|||||||
|
|
||||||
### Technology Stack
|
### Technology Stack
|
||||||
- **Backend**: Laravel 12 (PHP 8.4)
|
- **Backend**: Laravel 12 (PHP 8.4)
|
||||||
- **Frontend**: Livewire + Alpine.js + Tailwind CSS
|
- **Frontend**: Livewire 3.5+ with Alpine.js and Tailwind CSS 4.1+
|
||||||
- **Database**: PostgreSQL 15
|
- **Database**: PostgreSQL 15 (primary), Redis 7 (cache/queues)
|
||||||
- **Cache/Queue**: Redis 7
|
|
||||||
- **Real-time**: Soketi (WebSocket server)
|
- **Real-time**: Soketi (WebSocket server)
|
||||||
- **Containerization**: Docker & Docker Compose
|
- **Containerization**: Docker & Docker Compose
|
||||||
|
- **Queue Management**: Laravel Horizon
|
||||||
|
|
||||||
### Key Components
|
### Key Components
|
||||||
|
|
||||||
#### Core Models
|
#### Core Models
|
||||||
- `Application` - Deployed applications with Git integration
|
- `Application` - Deployed applications with Git integration (74KB, highly complex)
|
||||||
- `Server` - Remote servers managed by Coolify
|
- `Server` - Remote servers managed by Coolify (46KB, complex)
|
||||||
- `Service` - Docker Compose services
|
- `Service` - Docker Compose services (58KB, complex)
|
||||||
- `Database` - Standalone database instances (PostgreSQL, MySQL, MongoDB, Redis, etc.)
|
- `Database` - Standalone database instances (PostgreSQL, MySQL, MongoDB, Redis, etc.)
|
||||||
- `Team` - Multi-tenancy support
|
- `Team` - Multi-tenancy support
|
||||||
- `Project` - Grouping of environments and resources
|
- `Project` - Grouping of environments and resources
|
||||||
|
- `Environment` - Environment isolation (staging, production, etc.)
|
||||||
|
|
||||||
#### Job System
|
#### Job System
|
||||||
- Uses Laravel Horizon for queue management
|
- Uses Laravel Horizon for queue management
|
||||||
@@ -67,27 +68,139 @@ Only run artisan commands inside "coolify" container when in development.
|
|||||||
- `app/Jobs/` - Background queue jobs
|
- `app/Jobs/` - Background queue jobs
|
||||||
- `app/Livewire/` - Frontend components (full-stack with Livewire)
|
- `app/Livewire/` - Frontend components (full-stack with Livewire)
|
||||||
- `app/Models/` - Eloquent models
|
- `app/Models/` - Eloquent models
|
||||||
|
- `app/Rules/` - Custom validation rules
|
||||||
|
- `app/Http/Middleware/` - HTTP middleware
|
||||||
- `bootstrap/helpers/` - Helper functions for various domains
|
- `bootstrap/helpers/` - Helper functions for various domains
|
||||||
- `database/migrations/` - Database schema evolution
|
- `database/migrations/` - Database schema evolution
|
||||||
|
- `routes/` - Application routing (web.php, api.php, webhooks.php, channels.php)
|
||||||
|
- `resources/views/livewire/` - Livewire component views
|
||||||
|
- `tests/` - Pest tests (Feature and Unit)
|
||||||
|
|
||||||
## Development Guidelines
|
## Development Guidelines
|
||||||
|
|
||||||
### Code Organization
|
### Frontend Philosophy
|
||||||
- Use Actions pattern for complex business logic
|
Coolify uses a **server-side first** approach with minimal JavaScript:
|
||||||
- Livewire components handle UI and user interactions
|
- **Livewire** for server-side rendering with reactive components
|
||||||
- Jobs handle asynchronous operations
|
- **Alpine.js** for lightweight client-side interactions
|
||||||
- Traits provide shared functionality (e.g., `ExecuteRemoteCommand`)
|
- **Tailwind CSS** for utility-first styling with dark mode support
|
||||||
|
- **Enhanced Form Components** with built-in authorization system
|
||||||
|
- Real-time updates via WebSocket without page refreshes
|
||||||
|
|
||||||
### Testing
|
### Livewire Component Structure
|
||||||
- Uses Pest for testing framework
|
- Components located in `app/Livewire/`
|
||||||
- Tests located in `tests/` directory
|
- Views in `resources/views/livewire/`
|
||||||
|
- State management handled on the server
|
||||||
|
- Use wire:model for two-way data binding
|
||||||
|
- Dispatch events for component communication
|
||||||
|
|
||||||
### Deployment and Docker
|
### Code Organization Patterns
|
||||||
- Applications are deployed using Docker containers
|
- **Actions Pattern**: Use Actions for complex business logic (`app/Actions/`)
|
||||||
- Configuration generated dynamically based on application settings
|
- **Livewire Components**: Handle UI and user interactions
|
||||||
- Supports multiple deployment targets and proxy configurations
|
- **Jobs**: Handle asynchronous operations
|
||||||
|
- **Traits**: Provide shared functionality (e.g., `ExecuteRemoteCommand`)
|
||||||
|
- **Helper Functions**: Domain-specific helpers in `bootstrap/helpers/`
|
||||||
|
|
||||||
## Cloud instance
|
### Database Patterns
|
||||||
We have a cloud instance of Coolify, a hosted Coolify.
|
- Use Eloquent ORM for database interactions
|
||||||
This consists of 2 horizon worker servers.
|
- Implement relationships properly (HasMany, BelongsTo, etc.)
|
||||||
It has thousands of servers connected, thousands of users, so make sure develop features to that usecase.
|
- Use database transactions for critical operations
|
||||||
|
- Leverage query scopes for reusable queries
|
||||||
|
- Apply indexes for performance-critical queries
|
||||||
|
|
||||||
|
### Security Best Practices
|
||||||
|
- **Authentication**: Multi-provider auth via Laravel Fortify & Sanctum
|
||||||
|
- **Authorization**: Team-based access control with policies and enhanced form components
|
||||||
|
- **Form Component Security**: Built-in `canGate` authorization system for UI components
|
||||||
|
- **API Security**: Token-based auth with IP allowlisting
|
||||||
|
- **Secrets Management**: Never log or expose sensitive data
|
||||||
|
- **Input Validation**: Always validate user input with Form Requests or Rules
|
||||||
|
- **SQL Injection Prevention**: Use Eloquent ORM or parameterized queries
|
||||||
|
|
||||||
|
### API Development
|
||||||
|
- RESTful endpoints in `routes/api.php`
|
||||||
|
- Use API Resources for response formatting
|
||||||
|
- Implement rate limiting for public endpoints
|
||||||
|
- Version APIs when making breaking changes
|
||||||
|
- Document endpoints with clear examples
|
||||||
|
|
||||||
|
### Testing Strategy
|
||||||
|
- **Framework**: Pest for expressive testing
|
||||||
|
- **Structure**: Feature tests for user flows, Unit tests for isolated logic
|
||||||
|
- **Coverage**: Test critical paths and edge cases
|
||||||
|
- **Mocking**: Use Laravel's built-in mocking for external services
|
||||||
|
- **Database**: Use RefreshDatabase trait for test isolation
|
||||||
|
|
||||||
|
### Routing Conventions
|
||||||
|
- Group routes by middleware and prefix
|
||||||
|
- Use route model binding for cleaner controllers
|
||||||
|
- Name routes consistently (resource.action)
|
||||||
|
- Implement proper HTTP verbs (GET, POST, PUT, DELETE)
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
- Use `handleError()` helper for consistent error handling
|
||||||
|
- Log errors with appropriate context
|
||||||
|
- Return user-friendly error messages
|
||||||
|
- Implement proper HTTP status codes
|
||||||
|
|
||||||
|
### Performance Considerations
|
||||||
|
- Use eager loading to prevent N+1 queries
|
||||||
|
- Implement caching for frequently accessed data
|
||||||
|
- Queue heavy operations
|
||||||
|
- Optimize database queries with proper indexes
|
||||||
|
- Use chunking for large data operations
|
||||||
|
|
||||||
|
### Code Style
|
||||||
|
- Follow PSR-12 coding standards
|
||||||
|
- Use Laravel Pint for automatic formatting
|
||||||
|
- Write descriptive variable and method names
|
||||||
|
- Keep methods small and focused
|
||||||
|
- Document complex logic with clear comments
|
||||||
|
|
||||||
|
## Cloud Instance Considerations
|
||||||
|
|
||||||
|
We have a cloud instance of Coolify (hosted version) with:
|
||||||
|
- 2 Horizon worker servers
|
||||||
|
- Thousands of connected servers
|
||||||
|
- Thousands of active users
|
||||||
|
- High-availability requirements
|
||||||
|
|
||||||
|
When developing features:
|
||||||
|
- Consider scalability implications
|
||||||
|
- Test with large datasets
|
||||||
|
- Implement efficient queries
|
||||||
|
- Use queues for heavy operations
|
||||||
|
- Consider rate limiting and resource constraints
|
||||||
|
- Implement proper error recovery mechanisms
|
||||||
|
|
||||||
|
## Important Reminders
|
||||||
|
|
||||||
|
- Always run code formatting: `./vendor/bin/pint`
|
||||||
|
- Test your changes: `./vendor/bin/pest`
|
||||||
|
- Check for static analysis issues: `./vendor/bin/phpstan`
|
||||||
|
- Use existing patterns and helpers
|
||||||
|
- Follow the established directory structure
|
||||||
|
- Maintain backward compatibility
|
||||||
|
- Document breaking changes
|
||||||
|
- Consider performance impact on large-scale deployments
|
||||||
|
|
||||||
|
## Additional Documentation
|
||||||
|
|
||||||
|
For more detailed guidelines and patterns, refer to the `.cursor/rules/` directory:
|
||||||
|
|
||||||
|
### Architecture & Patterns
|
||||||
|
- [Application Architecture](.cursor/rules/application-architecture.mdc) - Detailed application structure
|
||||||
|
- [Deployment Architecture](.cursor/rules/deployment-architecture.mdc) - Deployment patterns and flows
|
||||||
|
- [Database Patterns](.cursor/rules/database-patterns.mdc) - Database design and query patterns
|
||||||
|
- [Frontend Patterns](.cursor/rules/frontend-patterns.mdc) - Livewire and Alpine.js patterns
|
||||||
|
- [API & Routing](.cursor/rules/api-and-routing.mdc) - API design and routing conventions
|
||||||
|
|
||||||
|
### Development & Security
|
||||||
|
- [Development Workflow](.cursor/rules/development-workflow.mdc) - Development best practices
|
||||||
|
- [Security Patterns](.cursor/rules/security-patterns.mdc) - Security implementation details
|
||||||
|
- [Form Components](.cursor/rules/form-components.mdc) - Enhanced form components with authorization
|
||||||
|
- [Testing Patterns](.cursor/rules/testing-patterns.mdc) - Testing strategies and examples
|
||||||
|
|
||||||
|
### Project Information
|
||||||
|
- [Project Overview](.cursor/rules/project-overview.mdc) - High-level project structure
|
||||||
|
- [Technology Stack](.cursor/rules/technology-stack.mdc) - Detailed tech stack information
|
||||||
|
- [Cursor Rules Guide](.cursor/rules/cursor_rules.mdc) - How to maintain cursor rules
|
||||||
Reference in New Issue
Block a user