Implementiert einen sicheren Weg zum Entfernen von Modulen aus dem System: Features: - Zeigt Modul-Informationen (Plugin + Datenbank) an - Widerruft Lizenz automatisch (optional mit --keep-license) - Löscht Datenbank-Eintrag im Permission-System (optional mit --keep-db-entry) - Warnt vor verknüpften Permissions - Zeigt nächste manuelle Schritte (Composer, Bundle, Migrations, Cache) - Bestätigung erforderlich (überspringbar mit --force) Dokumentation: - Command-Hilfe in src/Command/ModuleRemoveCommand.php - Benutzer-Anleitung in docs/PLUGIN_QUICKSTART.md - Entwickler-Referenz in CLAUDE.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
416 lines
12 KiB
Markdown
416 lines
12 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project Overview
|
|
|
|
myCRM is a modern, modular CRM application built with Symfony 7.1 LTS, Vue.js 3, and PrimeVue. It features a sophisticated permission system, API Platform for REST APIs, and a single-page application architecture.
|
|
|
|
## Common Development Commands
|
|
|
|
### Initial Setup
|
|
```bash
|
|
composer install
|
|
php bin/console doctrine:database:create
|
|
php bin/console doctrine:migrations:migrate
|
|
php bin/console doctrine:fixtures:load # Load test data (admin@mycrm.local/admin123)
|
|
npm install
|
|
npm run dev
|
|
```
|
|
|
|
### Development Workflow
|
|
```bash
|
|
# Terminal 1: Backend server
|
|
symfony serve -d # OR: php -S localhost:8000 -t public/
|
|
|
|
# Terminal 2: Frontend with hot reload
|
|
npm run watch
|
|
|
|
# Production build
|
|
npm run build
|
|
```
|
|
|
|
### Database Operations
|
|
```bash
|
|
# Create new migration after entity changes
|
|
php bin/console make:migration
|
|
php bin/console doctrine:migrations:migrate
|
|
|
|
# Validate schema matches entities
|
|
php bin/console doctrine:schema:validate
|
|
|
|
# Reset database (dev only)
|
|
php bin/console doctrine:database:drop --force
|
|
php bin/console doctrine:database:create
|
|
php bin/console doctrine:migrations:migrate
|
|
php bin/console doctrine:fixtures:load
|
|
```
|
|
|
|
### Testing
|
|
```bash
|
|
# Backend tests
|
|
php bin/phpunit
|
|
|
|
# Validate Doctrine schema
|
|
php bin/console doctrine:schema:validate
|
|
|
|
# Check user permissions (custom command)
|
|
php bin/console app:user:permissions <email>
|
|
```
|
|
|
|
### Code Generation
|
|
```bash
|
|
# Entity with API Platform support
|
|
php bin/console make:entity
|
|
|
|
# Migration after entity changes
|
|
php bin/console make:migration
|
|
|
|
# Security voter
|
|
php bin/console make:voter
|
|
|
|
# API Platform state processor/provider
|
|
php bin/console make:state-processor
|
|
php bin/console make:state-provider
|
|
```
|
|
|
|
### Cache Management
|
|
```bash
|
|
php bin/console cache:clear
|
|
APP_ENV=prod php bin/console cache:warmup
|
|
```
|
|
|
|
### Module Management (Plugin System)
|
|
```bash
|
|
# List all installed modules with license status
|
|
php bin/console app:module:list
|
|
|
|
# Register/manage module license
|
|
php bin/console app:module:license billing # Interactive
|
|
php bin/console app:module:license billing <license-key> # Direct
|
|
php bin/console app:module:license billing --validate # Validate
|
|
php bin/console app:module:license billing --revoke # Revoke
|
|
|
|
# Remove a module safely
|
|
php bin/console app:module:remove billing # Interactive
|
|
php bin/console app:module:remove billing --force # No confirmation
|
|
php bin/console app:module:remove billing --keep-license # Keep license
|
|
php bin/console app:module:remove billing --keep-db-entry # Keep DB entry
|
|
```
|
|
|
|
## Architecture Overview
|
|
|
|
### Security System (6 Layers)
|
|
|
|
The application uses a sophisticated multi-layer security architecture:
|
|
|
|
1. **Authentication Layer**: Symfony form login + OAuth (PocketId integration)
|
|
2. **Standard Roles**: `ROLE_ADMIN`, `ROLE_USER` (stored in User.roles as JSON array)
|
|
3. **Module Permission System**: Custom fine-grained permissions
|
|
- User → UserRoles (many-to-many) → Role → RolePermissions → Module
|
|
- 6 permission types: view, create, edit, delete, export, manage
|
|
- Checked via: `$user->hasModulePermission('contacts', 'view')`
|
|
4. **Custom Voters**: Entity-level access control (4 voters)
|
|
- `ModuleVoter`: Routes permission checks to module system
|
|
- `ProjectVoter`: Project ownership + team member checks
|
|
- `ProjectTaskVoter`: Inherits project access + admin bypass
|
|
- `GitRepositoryVoter`: Requires associated project access
|
|
5. **Event Listeners**: Pre-persist security validation
|
|
- `ProjectTaskSecurityListener`: Validates project tasks can only be created if user has project access
|
|
6. **Query Extensions**: Auto-filter collections
|
|
- `ProjectAccessExtension`: Automatically filters project collections based on user access
|
|
|
|
### Permission Check Flow
|
|
|
|
```php
|
|
// 1. Check via Security component
|
|
$this->denyAccessUnlessGranted('view', $contact); // Uses ContactVoter → ModuleVoter
|
|
|
|
// 2. Direct module check
|
|
if ($user->hasModulePermission('contacts', 'edit')) { }
|
|
|
|
// 3. API Platform security
|
|
#[ApiResource(
|
|
operations: [
|
|
new Get(security: "is_granted('VIEW', object)")
|
|
]
|
|
)]
|
|
```
|
|
|
|
### Entity Relationships
|
|
|
|
**Core Entities:**
|
|
- **User**: Authentication + role management (dual system: Symfony roles + custom UserRoles)
|
|
- **Role**: Groups of module permissions
|
|
- **Module**: CRM modules (contacts, projects, tasks, etc.)
|
|
- **RolePermission**: Joins Role ↔ Module with 6 boolean flags
|
|
- **Contact**: Business contacts (implements ModuleAwareInterface, module='contacts')
|
|
- **ContactPerson**: Linked to Contact, inherits permissions
|
|
- **Project**: Project management (owner + team members, module='projects')
|
|
- **ProjectTask**: Subtasks under projects (standalone tasks admin-only, module='tasks')
|
|
- **GitRepository**: Linked to projects, inherits project access
|
|
|
|
**Key Patterns:**
|
|
- **ModuleAwareInterface**: Entities that tie to permission modules
|
|
- **Project Access Model**: `$project->hasAccess($user)` returns true if owner OR team member
|
|
- **Permission Inheritance**: ContactPerson inherits Contact permissions, GitRepository inherits Project permissions
|
|
- **Dual Role System**: User.roles (Symfony standard array) + User.userRoles (ManyToMany with Role entity)
|
|
|
|
### API Platform Configuration
|
|
|
|
**Per-Entity Security:**
|
|
```php
|
|
#[ApiResource(
|
|
operations: [
|
|
new GetCollection(security: "is_granted('VIEW', 'contacts')"),
|
|
new Get(security: "is_granted('VIEW', object)"),
|
|
new Post(security: "is_granted('CREATE', 'contacts')"),
|
|
new Put(security: "is_granted('EDIT', object)"),
|
|
new Delete(security: "is_granted('DELETE', object)")
|
|
]
|
|
)]
|
|
```
|
|
|
|
**Serialization Groups:**
|
|
- `{entity}:read`: Fields exposed in GET responses
|
|
- `{entity}:write`: Fields accepted in POST/PUT
|
|
- Used to control field visibility based on context
|
|
|
|
**Common Filters:**
|
|
- `SearchFilter`: Text search on specific fields
|
|
- `BooleanFilter`: Filter by boolean fields (active, isDebtor, etc.)
|
|
- `DateFilter`: Filter by date ranges
|
|
- Custom filters for complex queries (e.g., ProjectTaskProjectFilter)
|
|
|
|
### Frontend Architecture (Vue.js 3)
|
|
|
|
**Directory Structure:**
|
|
```
|
|
/assets/js
|
|
/components - Reusable components (CrudDataTable, AppMenu, AppTopbar)
|
|
/views - Page-level components (ContactManagement, ProjectManagement, Dashboard)
|
|
/layout - Sakai layout components (AppLayout, AppSidebar)
|
|
/composables - Composition API functions (useAuth, usePermissions)
|
|
/stores - Pinia state management
|
|
/api - API client wrappers
|
|
/router.js - Vue Router SPA navigation
|
|
|
|
/assets/styles
|
|
/layout - Sakai SCSS (topbar, sidebar, menu, responsive)
|
|
tailwind.css - Tailwind CSS v4 with PrimeUI plugin
|
|
sakai.scss - Sakai layout imports
|
|
```
|
|
|
|
**Component Pattern:**
|
|
```vue
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
import CrudDataTable from '@/components/CrudDataTable.vue'
|
|
|
|
const items = ref([])
|
|
const apiEndpoint = '/api/contacts'
|
|
|
|
async function loadData() {
|
|
const response = await fetch(apiEndpoint)
|
|
items.value = await response.json()
|
|
}
|
|
</script>
|
|
```
|
|
|
|
**PrimeVue Usage:**
|
|
- `DataTable`: Main component for entity lists (server-side pagination, filtering, sorting)
|
|
- `Dialog`: Modal forms for create/edit
|
|
- `Button`, `InputText`, `Dropdown`: Form components
|
|
- `Chart`: Dashboard visualizations (Chart.js integration)
|
|
- Theme: Aura theme with dark mode support
|
|
|
|
**Webpack Encore:**
|
|
- Entry point: `assets/app.js`
|
|
- Aliases: `@` → `assets/js`, `@images` → `assets/images`
|
|
- Hot reload: `npm run watch`
|
|
- Production build: `npm run build`
|
|
|
|
## Development Conventions
|
|
|
|
### Backend (Symfony)
|
|
|
|
**Controllers**: Keep thin, delegate to services
|
|
```php
|
|
// Good
|
|
public function create(Request $request, ContactService $service): JsonResponse
|
|
{
|
|
$this->denyAccessUnlessGranted('CREATE', 'contacts');
|
|
return $this->json($service->createContact($request->toArray()));
|
|
}
|
|
```
|
|
|
|
**Services**: Constructor injection, use interfaces
|
|
```php
|
|
class ContactService
|
|
{
|
|
public function __construct(
|
|
private EntityManagerInterface $em,
|
|
private EventDispatcherInterface $dispatcher
|
|
) {}
|
|
}
|
|
```
|
|
|
|
**Entities**: Doctrine attributes, define relationships
|
|
```php
|
|
#[ORM\Entity]
|
|
#[ORM\Table(name: 'contacts')]
|
|
#[ApiResource(/* ... */)]
|
|
class Contact implements ModuleAwareInterface
|
|
{
|
|
public function getModuleName(): string { return 'contacts'; }
|
|
}
|
|
```
|
|
|
|
**Voters**: Implement granular permissions
|
|
```php
|
|
class ContactVoter extends Voter
|
|
{
|
|
protected function supports(string $attribute, mixed $subject): bool
|
|
{
|
|
return in_array($attribute, ['VIEW', 'EDIT', 'DELETE'])
|
|
&& ($subject instanceof Contact || $subject === 'contacts');
|
|
}
|
|
|
|
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
|
{
|
|
// Delegate to ModuleVoter via hasModulePermission()
|
|
}
|
|
}
|
|
```
|
|
|
|
### Frontend (Vue.js)
|
|
|
|
**Composables**: Extract reusable logic
|
|
```javascript
|
|
// composables/usePermissions.js
|
|
export function usePermissions() {
|
|
const hasPermission = (module, action) => {
|
|
// Check user permissions
|
|
}
|
|
return { hasPermission }
|
|
}
|
|
```
|
|
|
|
**Components**: Single File Components with Composition API
|
|
```vue
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
|
|
const props = defineProps({
|
|
apiEndpoint: String
|
|
})
|
|
|
|
const emit = defineEmits(['save', 'cancel'])
|
|
|
|
// Component logic here
|
|
</script>
|
|
```
|
|
|
|
## Testing Strategy
|
|
|
|
- **PHPUnit**: Backend unit and functional tests
|
|
- **Doctrine Schema Validation**: Run in CI to catch schema drift
|
|
- **Voter Tests**: Explicitly test permission logic
|
|
- **Frontend**: Vitest/Jest for Vue components (when configured)
|
|
|
|
## Important Files
|
|
|
|
**Security:**
|
|
- `src/Security/Voter/` - All voter implementations
|
|
- `src/EventListener/ProjectTaskSecurityListener.php` - Pre-persist validation
|
|
- `src/Filter/ProjectAccessExtension.php` - Query auto-filtering
|
|
- `src/Entity/User.php` - hasModulePermission() method (lines ~200-220)
|
|
|
|
**API Configuration:**
|
|
- Entity `#[ApiResource]` attributes - Security, operations, filters
|
|
- `config/packages/api_platform.yaml` - Global API settings
|
|
|
|
**Frontend Entry:**
|
|
- `assets/app.js` - Vue app initialization
|
|
- `assets/js/router.js` - Route definitions and navigation guards
|
|
- `webpack.config.js` - Encore configuration
|
|
|
|
## Key Workflows
|
|
|
|
### Adding a New Module
|
|
|
|
1. Create entity with `ModuleAwareInterface`
|
|
2. Add to Module entity fixtures/database
|
|
3. Create voter (or rely on ModuleVoter)
|
|
4. Configure API Platform security
|
|
5. Add Vue.js view component
|
|
6. Add route to router.js
|
|
7. Add menu item to AppMenu.vue
|
|
|
|
### Adding a New Permission-Controlled Feature
|
|
|
|
1. Determine permission level (module vs. entity)
|
|
2. If entity-level: create/update Voter
|
|
3. Add security checks in controller/API
|
|
4. Update frontend to check permissions before showing UI
|
|
5. Test with different user roles
|
|
|
|
### Database Schema Changes
|
|
|
|
1. Modify entity attributes
|
|
2. `php bin/console make:migration`
|
|
3. Review generated migration
|
|
4. `php bin/console doctrine:migrations:migrate`
|
|
5. `php bin/console doctrine:schema:validate` to verify
|
|
6. Update fixtures if needed
|
|
|
|
## Common Patterns
|
|
|
|
**Check Module Permission (Backend):**
|
|
```php
|
|
if (!$user->hasModulePermission('contacts', 'view')) {
|
|
throw new AccessDeniedException();
|
|
}
|
|
```
|
|
|
|
**Check Entity Permission (Backend):**
|
|
```php
|
|
$this->denyAccessUnlessGranted('EDIT', $contact);
|
|
```
|
|
|
|
**API Platform Security:**
|
|
```php
|
|
#[ApiResource(
|
|
security: "is_granted('ROLE_USER')",
|
|
operations: [
|
|
new Get(security: "is_granted('VIEW', object)")
|
|
]
|
|
)]
|
|
```
|
|
|
|
**Project Access Check:**
|
|
```php
|
|
if (!$project->hasAccess($user)) {
|
|
throw new AccessDeniedException();
|
|
}
|
|
```
|
|
|
|
**Frontend Permission Check:**
|
|
```javascript
|
|
import { usePermissions } from '@/composables/usePermissions'
|
|
|
|
const { hasPermission } = usePermissions()
|
|
|
|
if (hasPermission('contacts', 'create')) {
|
|
// Show create button
|
|
}
|
|
```
|
|
|
|
## Additional Documentation
|
|
|
|
- `docs/LOGIN.md` - Authentication system details
|
|
- `docs/PERMISSIONS.md` - Modular permission system
|
|
- `docs/USER-CRUD.md` - User management with API Platform
|
|
- `docs/PROJECT_TASKS_MODULE.md` - Project tasks implementation
|
|
- `.github/copilot-instructions.md` - Detailed development guidelines
|