Fixes a critical bug in the environment variable autocomplete component
where arrow key navigation could cause divide-by-zero errors when the
suggestions array is empty.
Changes:
- Add early guard in handleKeydown to check for empty suggestions array
before performing modulo operations
- Remove unreachable "No suggestions" template that could never display
- Add validation to hide dropdown when user types third brace ({{{)
- Refactor add.blade.php to use @if directives instead of x-show for
better performance and cleaner code
The fix ensures arrow keys do nothing when suggestions are empty,
preventing JavaScript errors while maintaining all existing functionality
including the scoped empty state messages with helpful links.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace hardcoded URL paths in getScopeUrl() with Laravel's route() helper
- Add scopeUrls property to EnvVarInput component with named routes
- Pass projectUuid and environmentUuid to enable context-specific environment links
- Environment scope link now navigates to the specific project/environment shared variables page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Exited containers don't run health checks, so showing "(unhealthy)" is
misleading. This fix ensures exited status displays without health
suffixes across all monitoring systems (SSH, Sentinel, services, etc.)
and at the UI layer for backward compatibility with existing data.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace border-based left indicator with inset box-shadow to prevent unwanted layout shifts when focusing or marking fields as dirty. The solution reserves 4px space with transparent shadow in default state and transitions to colored shadow on focus/dirty without affecting the box model. Update all form components (input, textarea, select, datalist) for consistency.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Introduced tests for ContainerStatusAggregator to validate status aggregation logic across various container states.
- Implemented tests to ensure serverStatus accessor correctly checks server infrastructure health without being affected by container status.
- Updated ExcludeFromHealthCheckTest to verify excluded status handling in various components.
- Removed obsolete PushServerUpdateJobStatusAggregationTest as its functionality is covered elsewhere.
- Updated version number for sentinel to 0.0.17 in versions.json.
- Add warning helper for 'unknown' health status
- Clarifies that no health check is configured
- Explains that Traefik/Caddy will still route traffic
- Recommends configuring health checks for better reliability
- Update warning helper for 'unhealthy' health status
- Corrects misleading message that suggested resource might work
- Clearly states health check is failing
- Emphasizes that Traefik will NOT work with unhealthy containers
- Highlights that user action is required
Both warnings include links to documentation and use consistent warning icons.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When all containers are excluded from health checks, display their actual status
with :excluded suffix instead of misleading hardcoded statuses. This prevents
broken UI state with incorrect action buttons and provides clarity that monitoring
is disabled.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Adds a new EnvVarInput component that provides autocomplete suggestions for shared environment variables from team, project, and environment scopes. Users can reference variables using {{ syntax.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
The modal-input component was using inline <style> blocks with ID selectors
to apply width constraints, which had inconsistent specificity and only
applied on lg+ breakpoints. This caused modals to appear full-width instead
of being properly constrained.
Replaced the inline style approach with Tailwind utility classes following
the pattern used in modal-confirmation component:
- Removed inline <style> block with media queries
- Added min-w-full and lg:min-w-[{minWidth}] for responsive minimum width
- Added max-w-[{maxWidth}] and max-h-[calc(100vh-2rem)] for size constraints
This ensures consistent modal sizing across all breakpoints and fixes the
full-width modal issue reported when adding shared environment variables.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit introduces several improvements to the Traefik version tracking
feature and proxy configuration UI:
## Caching Improvements
1. **New centralized helper functions** (bootstrap/helpers/versions.php):
- `get_versions_data()`: Redis-cached access to versions.json (1 hour TTL)
- `get_traefik_versions()`: Extract Traefik versions from cached data
- `invalidate_versions_cache()`: Clear cache when file is updated
2. **Performance optimization**:
- Single Redis cache key: `coolify:versions:all`
- Eliminates 2-4 file reads per page load
- 95-97.5% reduction in disk I/O time
- Shared cache across all servers in distributed setup
3. **Updated all consumers to use cached helpers**:
- CheckTraefikVersionJob: Use get_traefik_versions()
- Server/Proxy: Two-level caching (Redis + in-memory per-request)
- CheckForUpdatesJob: Auto-invalidate cache after updating file
- bootstrap/helpers/shared.php: Use cached data for Coolify version
## UI/UX Improvements
1. **Navbar warning indicator**:
- Added yellow warning triangle icon next to "Proxy" menu item
- Appears when server has outdated Traefik version
- Uses existing traefik_outdated_info data for instant checks
- Provides at-a-glance visibility of version issues
2. **Proxy sidebar persistence**:
- Fixed sidebar disappearing when clicking "Switch Proxy"
- Configuration link now always visible (needed for proxy selection)
- Dynamic Configurations and Logs only show when proxy is configured
- Better navigation context during proxy switching workflow
## Code Quality
- Added comprehensive PHPDoc for Server::$traefik_outdated_info property
- Improved code organization with centralized helper approach
- All changes formatted with Laravel Pint
- Maintains backward compatibility
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add automated Traefik version checking job running weekly on Sundays
- Implement version detection from running containers and comparison with versions.json
- Add notifications across all channels (Email, Discord, Slack, Telegram, Pushover, Webhook) for outdated versions
- Create dismissible callout component with localStorage persistence
- Display cross-branch upgrade warnings (e.g., v3.5 -> v3.6) with changelog links
- Show patch update notifications within same branch
- Add warning icon that appears when callouts are dismissed
- Prevent duplicate notifications during proxy restart by adding restarting parameter
- Fix notification spam with transition-based logic for status changes
- Enable system email settings by default in development mode
- Track last saved/applied proxy settings to detect configuration drift
Previous approach used invalid inline styles with @media queries,
which browsers ignore. Now using:
- Unique modal ID generated with PHP uniqid()
- <style> tag with proper CSS media queries
- ID selector for scoped styling
This properly constrains modal width on large screens while keeping
full width on mobile.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Tailwind JIT compiler doesn't compile dynamically interpolated classes
like lg:max-w-[{{ $maxWidth }}]. Switched to inline styles with media
queries to support dynamic min/max width values.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The previous fix reduced minWidth but modal still expanded to fit
content. Now:
- Added maxWidth prop to modal-input component (default 48rem)
- Set Edit Domains modal to minWidth=32rem, maxWidth=40rem
- This constrains the modal to a reasonable size range
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Track container restart counts from Docker and detect crash loops to provide better visibility into application health issues.
- Add restart_count, last_restart_at, and last_restart_type columns to applications table
- Detect restart count increases from Docker inspect data and send notifications
- Show restart count badge in UI with warning icon on Logs navigation
- Distinguish between crash restarts and manual restarts
- Implement 30-second grace period to prevent false "exited" status during crash loops
- Reset restart count on manual stop, restart, and redeploy actions
- Add unit tests for restart count tracking logic
This helps users quickly identify when containers are in crash loops and need attention, even when the container status flickers between states during Docker's restart backoff period.
- Remove wire:ignore from modal-input.blade.php wrapper to allow child Livewire components to be properly tracked
- Add unique wire:key to EditCompose component for proper identification when teleported
- Fixes 'Unable to call component method' error when saving compose files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit fixes two related issues preventing the Monaco editor from displaying Docker Compose file content:
1. Data Sync Issue:
- After loadComposeFile() fetches the compose content from Git and updates the database model, the Livewire component properties were never synced
- Monaco editor binds to component properties via wire:model, so it remained empty
- Fixed by calling syncFromModel() after refresh() in loadComposeFile() method
2. Script Duplication Issue:
- Multiple Monaco editors on the same page (compose files, dockerfile, labels) caused race condition
- Each instance tried to inject the Monaco loader script simultaneously
- Resulted in "SyntaxError: Identifier '_amdLoaderGlobal' has already been declared"
- Fixed by adding a global flag to prevent duplicate script injection
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Alpine.js reactive height calculation based on viewport size
- Monaco editor now responds to window resize events
- Fix Livewire component structure by moving style tag inside root div
- Update CLAUDE.md to document critical single root element requirement
- Set minimum editor height of 300px with responsive maximum
- Use CSS custom properties to pass calculated height to components
Fixes#6974
- Add max-height constraints to prevent modals from exceeding viewport
- Enable vertical scrolling with overflow-y-auto on content areas
- Improve mobile responsiveness with consistent padding
- Separate fixed header from scrollable content using flexbox
- Add touch scrolling support for iOS devices
This ensures buttons like "Back" and "Continue" remain accessible
on small devices by allowing users to scroll the modal content.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The loading icon was appearing during automatic background status checks
(every 10 seconds) even when users didn't click anything, which caused
confusion and made it seem like something was running unexpectedly.
Changes:
- Added manualCheckStatus() method to Application, Database, and Service
Heading components that wraps the checkStatus() call
- Updated status component buttons to call manualCheckStatus() instead
of checkStatus()
- Added wire:target="manualCheckStatus" to loading directives so the
loading icon only appears when users explicitly click the refresh button
- Added delay.shortest to prevent flickering on fast operations
The automatic wire:poll.10000ms="checkStatus" now runs silently in the
background without showing the loading icon, while manual refreshes
still provide visual feedback to the user.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit improves the visual presentation of service, application, and database logos on the new resource selection page:
- Remove grayscale filter: Logos now display in their original colors by default instead of being greyed out
- Dark mode support for SVGs: Updated SVG logos to use `fill="currentColor"` and added `text-black dark:text-white` wrapper for proper light/dark theme adaptation
- Consistent aspect ratios: Removed `aspect-square` and added `object-contain` to preserve original logo proportions
- Uniform sizing: Implemented fixed-size container (4.5rem × 4.5rem) with centered logo positioning to ensure all logos appear at consistent sizes regardless of intrinsic dimensions
- Improved mobile UX: Adjusted sticky search bar positioning from `top-10` to `top-20` to prevent navbar overlap
Files modified:
- resources/views/livewire/project/new/select.blade.php
- resources/views/components/resource-view.blade.php
- app/Livewire/Project/New/Select.php
- public/svgs/*.svg (12 SVG files updated with currentColor)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add ownedAndOnlySShKeys() method to filter out git-related keys
- Update Boarding component to use new filtering method
- Enhance datalist component with better multi-select and single-select handling
- Fix Alpine.js reactivity and improve UI interactions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The wire:dirty.class was being applied to all form inputs, even those without
wire:model bindings (like readonly fields). This caused the dirty state indicator
to appear on readonly fields when other fields in the form were modified.
Fixed by only applying wire:dirty.class when wire:model binding is present:
- input.blade.php: Moved wire:dirty.class inside @if($modelBinding !== 'null')
- textarea.blade.php: Applied same fix for all textarea variations
- select.blade.php: Applied same fix for select elements
This ensures only fields with actual Livewire bindings show dirty state indicators.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Resolved merge conflicts between Livewire model binding refactoring and UI/CSS updates from next branch. Key integrations:
- Preserved unique HTML ID generation for form components
- Maintained wire:model bindings using $modelBinding
- Integrated new wire:dirty.class styles (border-l-warning pattern)
- Kept both syncData(true) and validateDockerComposeForInjection in StackForm
- Merged security tests and helper improvements from next
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem:**
Monaco editor was receiving unique HTML IDs (e.g., "customLabels-a09a7773")
and using them in @entangle(), causing errors:
"Livewire property ['customLabels-a09a7773'] cannot be found"
**Root Cause:**
Monaco editor template uses @entangle($id) to bind to Livewire properties.
After our unique ID fix, $id contained the unique HTML ID with suffix,
not the original property name.
**Solution:**
Pass $modelBinding (original property name) instead of $htmlId to Monaco
editor component. This ensures @entangle() uses the correct property name
while HTML elements still get unique IDs.
**Result:**
✅ Monaco editor @entangle works correctly
✅ HTML IDs remain unique
✅ No Livewire property errors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Resolve browser console warnings about non-unique HTML IDs when multiple
Livewire components with similar form fields appear on the same page.
**Problem:**
Multiple forms using generic IDs like `id="description"` or `id="name"`
caused duplicate ID warnings and potential accessibility/JavaScript issues.
**Solution:**
- Separate `wire:model` binding name from HTML `id` attribute
- Auto-prefix HTML IDs with Livewire component ID for uniqueness
- Preserve existing `wire:model` behavior with property names
**Implementation:**
- Added `$modelBinding` property for wire:model (e.g., "description")
- Added `$htmlId` property for unique HTML ID (e.g., "lw-xyz123-description")
- Updated render() method to generate unique IDs automatically
- Updated all blade templates to use new properties
**Components Updated:**
- Input (text, password, etc.)
- Textarea (including Monaco editor)
- Select
- Checkbox
- Datalist (single & multiple selection)
**Result:**
✅ All HTML IDs now unique across page
✅ No console warnings
✅ wire:model bindings work correctly
✅ Validation error messages display correctly
✅ Backward compatible - no changes needed in existing components
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Hetzner Cloud server creation option to onboarding flow
- Change grid from 2 to 3 columns to accommodate all server options
- Mark both Hetzner and Remote Server as "Recommended"
- Fix Hetzner card height to match other cards
- Remove "select existing server" phase - onboarding always creates new servers
- Fix project loading on page refresh in Project Setup phase
- Fix browser back button navigation - remove aggressive restartBoarding() call
- Fix SSH key dropdown to not auto-select first key - require explicit selection
- Make checkpoint titles more prominent across all phases
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add centered, card-based layout with clean design
- Implement 3-step progress indicator component
- Add proper dark/light mode support following Coolify design system
- Implement Livewire URL state persistence for browser navigation
- Separate private key textareas for "Generate" vs "Add your own" modes
- Consistent checkpoint styling across all onboarding phases
- Enhanced typography with prominent titles (semibold, white in dark mode)
- Fixed state restoration on page refresh and browser back/forward navigation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhanced the terminal server/container selection with a new searchable datalist component:
**Terminal View Changes:**
- Replaced `x-forms.select` with `x-forms.datalist` for server/container selection
- Added search functionality for filtering servers and containers
- Fixed form validation by adding hidden input for proper HTML5 validation
- Prevented error messages when clearing selection (sets to 'default')
**Datalist Component (Single Selection):**
- Implemented Alpine.js-powered dropdown with search functionality
- Added visual dropdown arrow that rotates when opened
- Proper entangle binding for wire:model support
- Keyboard support (Escape to close)
- Click outside to close behavior
- Disabled options filtering (skips disabled options)
- Consistent styling with input/textarea components
**Styling Improvements:**
- Explicit background colors: `bg-white` (light) and `dark:bg-coolgray-100` (dark)
- Proper ring border: `ring-1 ring-inset ring-neutral-200 dark:ring-coolgray-300`
- Focus states: `focus-within:ring-2 focus-within:ring-coollabs dark:focus-within:ring-warning`
- Text colors: `text-black dark:text-white`
- Added custom scrollbar styling for dropdown lists
- Wire:dirty state support for visual feedback
- Proper padding and spacing (`py-1.5`, `px-1`, `px-2`)
**Multiple Selection Mode:**
- Also updated for consistent styling and scrollbar support
- Added proper background colors and focus states
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive cloud-init script management interface in the Security
section, allowing users to create, edit, delete, and reuse cloud-init
scripts across their team.
New Components:
- CloudInitScripts: Main listing page with grid view of scripts
- CloudInitScriptForm: Modal form for create/edit operations
Features:
- Create new cloud-init scripts with name and content
- Edit existing scripts
- Delete scripts with confirmation (requires typing script name)
- View script preview (first 200 characters)
- Scripts are encrypted in database
- Full authorization using CloudInitScriptPolicy
- Real-time updates via Livewire events
UI Location:
- Added to Security section nav: /security/cloud-init-scripts
- Positioned between Cloud Tokens and API Tokens
- Follows existing security UI patterns
Files Created:
- app/Livewire/Security/CloudInitScripts.php
- app/Livewire/Security/CloudInitScriptForm.php
- resources/views/livewire/security/cloud-init-scripts.blade.php
- resources/views/livewire/security/cloud-init-script-form.blade.php
Files Modified:
- routes/web.php - Added route
- resources/views/components/security/navbar.blade.php - Added nav link
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add basic infrastructure for custom webhook notifications:
- Create webhook_notification_settings table with event toggles
- Add WebhookNotificationSettings model with encrypted URL
- Integrate webhook settings into Team model and HasNotificationSettings trait
- Create Livewire component and Blade view for webhook configuration
- Add webhook navigation route and UI
This provides the foundation for sending webhook notifications to custom HTTP/HTTPS endpoints when events occur in Coolify.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Adjusted CSS styles in the navbar for improved readability and responsiveness.
- Updated the layout of the sidebar and navbar components to enhance user experience on different screen sizes.
- Ensured consistent alignment and spacing for elements within the navbar and sidebar.