Removed duplicate queue_application_deployment() call in Heading.php deploy method that was causing "Deployment already queued for this commit" error to display even though deployment was successfully queued.
Also changed notification type from 'success' to 'error' when deployment is actually skipped for proper user feedback.
Add detection system for PORT environment variable to help users configure applications correctly:
- Add detectPortFromEnvironment() method to Application model to detect PORT env var
- Add getDetectedPortInfoProperty() computed property in General Livewire component
- Display contextual info banners in UI when PORT is detected:
- Warning when PORT exists but ports_exposes is empty
- Warning when PORT doesn't match ports_exposes configuration
- Info message when PORT matches ports_exposes
- Add deployment logging to warn about PORT/ports_exposes mismatches
- Include comprehensive unit tests for port detection logic
The ports_exposes field remains authoritative for proxy configuration, while
PORT detection provides helpful suggestions to users.
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.
- Add retry configuration to CoolifyTask (3 tries, 600s timeout)
- Add retry configuration to ScheduledTaskJob (3 tries, configurable timeout)
- Add retry configuration to DatabaseBackupJob (2 tries)
- Implement exponential backoff for all jobs (30s, 60s, 120s intervals)
- Add failed() handlers with comprehensive error logging to scheduled-errors channel
- Add execution tracking: started_at, retry_count, duration (decimal), error_details
- Add configurable timeout field to scheduled tasks (60-3600s, default 300s)
- Update UI to include timeout configuration in task creation/editing forms
- Increase ScheduledJobManager lock expiration from 60s to 90s for high-load environments
- Implement safe queue cleanup with restart vs runtime modes
- Restart mode: aggressive cleanup (marks all processing jobs as failed)
- Runtime mode: conservative cleanup (only marks jobs >12h as failed, skips deployments)
- Add cleanup:redis --restart flag for system startup
- Integrate cleanup into Dev.php init() for development environment
- Increase scheduled-errors log retention from 7 to 14 days
- Create comprehensive test suite (unit and feature tests)
- Add TESTING_GUIDE.md with manual testing instructions
Fixes issues with jobs failing after single attempt and "attempted too many times" errors
- Added `requiredPort` property to `ServiceApplicationView` to track the required port for services.
- Introduced modal confirmation for removing required ports, including methods to confirm or cancel the action.
- Enhanced `Service` model with `getRequiredPort` and `requiresPort` methods to retrieve port information from service templates.
- Implemented `extractPortFromUrl` method in `ServiceApplication` to extract port from FQDN URLs.
- Updated frontend views to display warnings when required ports are missing from domains.
- Created unit tests for service port validation and extraction logic, ensuring correct behavior for various scenarios.
- Added feature tests for Livewire component handling of domain submissions with required ports.
- Fix SPA toggle not triggering nginx configuration regeneration by capturing old value before syncData
- Fix similar issue with is_http_basic_auth_enabled using value comparison instead of isDirty
- Remove redundant application settings save() call
- Add confirmation modal to nginx generation button to prevent accidental overwrites
- Pass correct type parameter (spa/static) to generateNginxConfiguration method
- Changed `$cast` to `$casts` in ApplicationSetting model to enable proper boolean casting for new fields.
- Added boolean fields: `is_spa`, `is_build_server_enabled`, `is_preserve_repository_enabled`, `is_container_label_escape_enabled`, `is_container_label_readonly_enabled`, and `use_build_secrets`.
fix: Update Livewire component to reflect new property names
- Updated references in the Livewire component for the new camelCase property names.
- Adjusted bindings and IDs for consistency with the updated model.
test: Add unit tests for ApplicationSetting boolean casting
- Created tests to verify boolean casting for `is_static` and other boolean fields in ApplicationSetting.
- Ensured all boolean fields are correctly defined in the casts array.
test: Implement tests for SynchronizesModelData trait
- Added tests to verify the functionality of the SynchronizesModelData trait, ensuring it correctly syncs properties between the component and the model.
- Included tests for handling non-existent properties gracefully.
- Add null check before updating OAuth settings to prevent calling methods on null
- Apply couldBeEnabled() validation for all settings in bulk update (not just instant save)
- Disable OAuth providers that fail validation and collect error messages
- Surface all validation errors to the user instead of silently failing
- Update oauth_settings_map with fresh data after saving each setting
This ensures bulk updates follow the same validation logic as instant-save paths
and prevents bypassing model validation by directly calling update.
🤖 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>
Fixed multiple issues with GitHub App source creation and management:
1. **Fixed null property assignment error on component mount**
- Changed property types to nullable in Change component (appId, installationId, clientId, etc.)
- Updated validation rules to allow nullable values
- Allows mounting component with newly created GitHub Apps that don't have these fields set yet
2. **Fixed Livewire morphing error on manual creation**
- Modified createGithubAppManually() to redirect after saving
- Prevents "Cannot read properties of null" error when view structure changes
- Fields now properly populated after manual creation without requiring page refresh
3. **Fixed is_system_wide not being saved on creation**
- Removed backwards logic that only saved is_system_wide on cloud instances
- Added is_system_wide to GithubApp model casts for proper boolean handling
- System-wide checkbox now works correctly on self-hosted instances
4. **Fixed misleading preview deployment checkbox**
- Removed instantSave attribute from permission checkboxes in unconfigured state
- These are configuration options for GitHub App creation, not database fields
- Prevents "GitHub App updated" success message when nothing was actually saved
5. **Added validation for Refetch Permissions button**
- Validates App ID and Private Key are set before attempting to fetch
- Shows clear error messages: "Cannot fetch permissions. Please set the following required fields first: App ID, Private Key"
- Prevents crash when private key is null or invalid
6. **Better error handling for unsupported private key formats**
- Detects OpenSSH format keys vs RSA PEM format
- Shows helpful message: "Please use an RSA private key in PEM format (BEGIN RSA PRIVATE KEY). OpenSSH format keys are not supported."
- GitHub Apps require RSA PEM format, not OpenSSH format
7. **Made GitHub App view mobile responsive**
- Updated all flex layouts to stack vertically on mobile (flex-col sm:flex-row)
- Form fields, buttons, and sections now properly responsive
- No more cut-off fields on small screens
Added comprehensive test coverage:
- GithubSourceChangeTest.php with 7 tests
- GithubSourceCreateTest.php with 6 tests
🤖 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 a searchable category dropdown filter on the new resource page to help users filter services by category.
Features:
- Category dropdown positioned next to search input
- Auto-focus on search field when dropdown opens
- Case-insensitive category filtering
- Proper acronym formatting (AI, API, CI, etc. displayed in uppercase)
- Loading/disabled state while categories are being fetched
- Category search/filter within dropdown
- Alphabetical sorting (case-insensitive)
Backend changes:
- Extract unique categories from service templates
- Handle comma-separated categories
- Format common acronyms to uppercase
- Case-insensitive natural sorting
Frontend changes:
- Searchable dropdown component with Alpine.js
- Category filter integration with existing search
- Disabled state placeholder during loading
- Auto-focus behavior for better UX
🤖 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>
When creating a Hetzner server from the onboarding view, the redirect
to the server details page was not working properly due to modal context.
The standard redirect() call doesn't handle navigation from within modals.
Changes:
- Add from_onboarding flag to ByHetzner component
- Use wire:navigate redirect when in onboarding mode
- Pass from_onboarding=true from boarding view
This ensures proper navigation to the newly created server page instead
of staying on the onboarding view.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When a docker compose service has multiple comma-separated domains, the
generate() method was only processing the first domain and truncating the rest.
The issue was that Url::fromString() can't parse comma-separated URLs - it only
parses the first one.
Fixed by:
1. Splitting comma-separated domains with explode(',', $domain_string)
2. Processing each domain individually in a foreach loop
3. Generating preview URLs for each domain using the same template/random/pr_id
4. Joining the results back with implode(',', $preview_fqdns)
This ensures all domains get properly transformed for preview deployments.
Example:
- Original: http://domain1.com,http://domain2.com
- Preview: http://57.domain1.com,http://57.domain2.com
- Before fix: http://57.domain1.com,http (truncated)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>