- Replace manual subscription create/update logic with updateOrCreate() and firstOrCreate() to eliminate race conditions
- Add validation in PricingPlans to prevent subscribing if team already has active subscription
- Improve error handling for missing team_id in customer.subscription.updated event
- Add tests verifying subscriptions are updated rather than duplicated
Allow serverLimit() and serverLimitReached() to accept an optional team
parameter instead of relying solely on the current session. This improves
testability and makes the methods more flexible by allowing them to work
without session state.
Add comprehensive tests covering various scenarios including no session,
team at limit, and team under limit.
Add validation in manual and normal webhook handlers to reject GitHub
event types other than 'push' and 'pull_request'. Unsupported events
now return a graceful response instead of potentially causing
downstream errors. Includes tests for ping events, unsupported event
types, and unknown events.
- Add storage endpoints (list, create, update, delete) to DatabasesController
- Add storage endpoints (list, create, update, delete) to ServicesController
- Add UUID field and migration for local_persistent_volumes table
- Update LocalPersistentVolume model to extend BaseModel
- Support UUID-based storage identification in ApplicationsController
- Update OpenAPI documentation with new storage endpoints and schemas
- Fix application name generation to extract repo name from full git path
- Add comprehensive tests for storage API operations
Extract the shouldRunNow() method from ScheduledJobManager and ServerManagerJob into
a reusable shouldRunCronNow() helper function. This centralizes cron scheduling logic
and enables consistent deduplication behavior across all scheduled job types.
- Create shouldRunCronNow() helper in bootstrap/helpers/shared.php with timezone
and dedup support
- Refactor ScheduledJobManager and ServerManagerJob to use the shared helper
- Add ScheduledJobDiagnostics command for inspecting cache state and scheduling
decisions across all scheduled jobs
- Simplify shouldRunNow tests to directly test the helper function
- Add DockerCleanupJob test for error handling and execution tracking
- Increase scheduled log retention from 1 to 7 days
Implements getPreviousRunDate() + cache-based tracking in shouldRunNow()
to prevent duplicate dispatch of scheduled jobs when queue delays push
execution past the cron minute. This resilience ensures jobs catch missed
windows without double-dispatching within the same cron window.
Updated scheduled job dispatches to include dedupKey parameter:
- Docker cleanup operations
- Server connection checks
- Sentinel restart checks
- Server storage checks
- Server patch checks
DockerCleanupJob now dispatches on the 'high' queue for faster processing.
Includes comprehensive test coverage for dedup behavior across different
cron schedules and delay scenarios.
Add support for optional comment field on environment variables created or
updated through the bulk API endpoints. Comments are validated to a maximum
of 256 characters and are nullable. Updates preserve existing comments when
not provided in the request.
Extract resource UUIDs from route parameters instead of request body
in ApplicationsController and ServicesController environment variable
endpoints. This prevents UUID parameters from being spoofed in the
request body.
- Replace $request->uuid with $request->route('uuid')
- Replace $request->env_uuid with $request->route('env_uuid')
- Add tests verifying route parameters are used and body UUIDs ignored
Add new Resources tab to storage show page displaying backup schedules using
that storage. Refactor storage show layout with navigation tabs for General
and Resources sections. Move delete action from form to show component.
Implement cascade deletion in S3Storage model to automatically disable S3
backups when storage is deleted. Improve error handling in DatabaseBackupJob
to throw exception when S3 storage is missing instead of silently returning.
- New Storage/Resources Livewire component
- Add resources.blade.php view
- Add storage.resources route
- Move delete() method from Form to Show component
- Add deleting event listener to S3Storage model
- Track backup count and current route in Show component
- Add #[On('submitStorage')] attribute to form submission
- Record refunds immediately before cancellation to prevent retry issues if cancel fails
- Wrap Stripe API calls in try-catch for refunds and quantity reverts with internal notifications
- Add null check in Team.subscriptionEnded() to prevent NPE when subscription doesn't exist
- Fix control flow bug in StripeProcessJob (add missing break statement)
- Cap dynamic server limit with MAX_SERVER_LIMIT in subscription updates
- Add comprehensive tests for refund failures, event handling, and null safety
Add current_period_end to refund eligibility checks and display next billing
date and billing interval in the subscription overview. Refactor the plan
overview layout to show subscription status more prominently.
Server metadata is now automatically gathered when server validation completes successfully, both in the async job and Livewire component. This ensures server details (OS, CPU count, etc.) are populated immediately after validation passes, improving the user experience without requiring manual metadata fetching.
Tests added to verify gatherServerMetadata is called on successful validation and skipped when validation fails.
The generate_preview_fqdn_compose method now extracts and populates the fqdn field from docker_compose_domains, making it available for webhook notifications. This handles multiple domains across services and gracefully sets fqdn to null when no domains are configured.
Add `is_container_label_escape_enabled` boolean field to services API,
allowing users to control whether special characters in container labels
are escaped. Defaults to true (escaping enabled).
When disabled, users can use environment variables within labels.
Includes validation rules and comprehensive test coverage.
- Add -i flag to explicitly specify ssh key path in git ls-remote operations
- Remove static $rules properties in favor of dynamic rules() method
- Fix test syntax error
Allow passing a custom GIT_SSH_COMMAND to setGitImportSettings() so that git fetch,
submodule update, and lfs pull use the same SSH authentication as the initial clone.
This is required for git sources like GitLab that use custom ports and identity files.
Also remove unnecessary SSH retry event tracking and add test coverage.
Ensure proper type comparison when verifying deployment team ownership.
Adds comprehensive feature tests for the GET /api/v1/deployments/{uuid} endpoint.
Extract shared variable resolution logic into a reusable helper function
`resolveSharedEnvironmentVariables()` and apply it in applicationParser and
serviceParser to ensure patterns like {{environment.VAR}}, {{project.VAR}},
and {{team.VAR}} are properly resolved in the compose environment section.
Without this, unresolved {{...}} strings would take precedence over resolved
values from the .env file (env_file:) in docker-compose configurations.
Add comprehensive test suite verifying that magic (referenced) SERVICE_URL_ and
SERVICE_FQDN_ variables don't overwrite values set by direct template declarations
or updateCompose(). Tests cover the fix for GitHub issue #8912 where generic
SERVICE_URL and SERVICE_FQDN variables remained stale after changing a service
domain in the UI. These tests ensure the transition from updateOrCreate() to
firstOrCreate() in the magic variables section works correctly.
Add ability to gather and display server system information including OS, architecture, kernel version, CPU count, memory, and uptime. Includes:
- New gatherServerMetadata() method to collect system details via remote commands
- New refreshServerMetadata() Livewire action with authorization and error handling
- Server Details UI section showing collected metadata with refresh capability
- Database migration to add server_metadata JSON column
- Comprehensive test suite for metadata collection and persistence
Validation operations should require write permissions as they trigger
state-changing actions. Updated middleware for:
- POST /api/v1/cloud-tokens/{uuid}/validate
- GET /api/v1/servers/{uuid}/validate
Added tests to verify read-only tokens cannot access these endpoints.
Previously, the view checked $privateKeyId with ! operator, which
incorrectly treats 0 (localhost key) as falsy. Changed to explicit
is_null() checks to distinguish between null (no key) and 0 (localhost).
Added test coverage for both cases.
Handle non-positive disable counts with `<= 0` so teams at or under the
server limit correctly re-enable force-disabled servers. Add a feature test
suite for ServerLimitCheckJob covering under-limit, at-limit, over-limit,
and no-op behavior.
Centralize min/max server limits in Stripe quantity updates and wire them into
Livewire subscription actions with price preview/update handling.
Also improve host/proxy middleware behavior by trusting loopback hosts when FQDN
is set and auto-enabling secure session cookies for HTTPS requests behind
proxies when session.secure is unset.
Includes feature tests for loopback trust and secure cookie auto-detection.
Introduce a new `UpdateSubscriptionQuantity` Stripe action to:
- preview prorated due-now and next-cycle recurring costs
- update subscription item quantity with proration invoicing
- revert quantity and void invoice when payment is not completed
Wire the flow into the Livewire subscription actions UI with a new adjust-limit modal,
price preview loading, and confirmation-based updates. Also refactor the subscription
management section layout and fix modal confirmation behavior for temporary 2FA bypass.
Add `Subscription::billingInterval()` helper and comprehensive Pest coverage for
quantity updates, preview calculations, failure/revert paths, and billing interval logic.
Enable `HasFactory` on `Environment`, `Project`, `ScheduledTask`, and
`StandaloneDocker`, and add dedicated factories for related models to
stabilize feature/unit tests.
Also bump `visus/cuid2` to `^6.0` and refresh `composer.lock` with the
resulting dependency updates.
- Refactor shouldRunNow() to only fire on first run (empty cache) if actually due by cron schedule, preventing spurious executions after cache loss or service restart
- Add enrichSkipLogsWithLinks() method to fetch and populate resource names and links for tasks, backups, and docker cleanup jobs in skip logs
- Update skip logs UI to display resource column with links to related resources, improving navigation and context
- Add fallback display when linked resources are deleted
- Expand tests to cover both restart scenarios: non-due jobs (should not fire) and due jobs (should fire)
- Implement pagination for skipped jobs display with 20 items per page
- Add pagination controls (previous/next buttons) to the scheduled jobs view
- Exclude ScheduledJobManager "started" events from run logs, keeping only "completed" events
- Add ShouldBeEncrypted interface to ScheduledTaskJob for secure queue handling
- Update log filtering to fetch 500 recent skips and slice for pagination
- Use Log facade instead of fully qualified class name
Implement dedup key-based cron tracking to make scheduled jobs resilient to queue
delays. Even if a job is delayed by minutes, it will catch the missed cron window
by tracking previousRunDate in cache instead of relying on isDue() alone.
- Add dedupKey parameter to shouldRunNow() in ScheduledJobManager
- When provided, uses getPreviousRunDate() + cache tracking for resilience
- Falls back to isDue() for docker cleanups without dedup key
- Prevents double-dispatch within same cron window
- Optimize ServerConnectionCheckJob dispatch
- Skip SSH checks if Sentinel is healthy (enabled and live)
- Reduces redundant checks when Sentinel heartbeat proves connectivity
- Remove hourly Sentinel update checks
- Consolidate to daily CheckAndStartSentinelJob dispatch
- Crash recovery handled by sentinelOutOfSync → ServerCheckJob flow
- Add logging for skipped database backups with context (backup_id, database_id, status)
- Refactor skip reason methods to accept server parameter, avoiding redundant queries
- Add comprehensive test suite for scheduling with various delay scenarios and timezones
Reduce unnecessary job queue pressure and improve subscription sync reliability:
- Cache ServerStorageCheckJob dispatch to only trigger on disk percentage changes
- Rate-limit ConnectProxyToNetworksJob to maximum once per 10 minutes
- Add progress callback support to SyncStripeSubscriptionsJob for UI feedback
- Implement bulk fetching of valid Stripe subscription IDs for efficiency
- Detect and report resubscribed users (same email, different customer ID)
- Fix CleanupUnreachableServers query operator (>= 3 instead of = 3)
- Improve empty subId validation in PushServerUpdateJob
- Optimize relationship access by using properties instead of query methods
- Add comprehensive test coverage for all optimizations
Properly escape commit SHA using escapeshellarg() before passing it
to shell commands. Add comprehensive tests for git commit rollback
scenarios including shallow clone, fallback behavior, and HEAD handling.
Add support for command-based health checks in addition to HTTP-based checks:
- New health_check_type field supporting 'http' and 'cmd' values
- New health_check_command field with strict regex validation
- Updated allowedFields in create_application and update_by_uuid endpoints
- Validation rules include max 1000 characters and safe character whitelist
- Added feature tests for health check API endpoints
- Added unit tests for GithubAppPolicy and SharedEnvironmentVariablePolicy
- Add regex validation to restrict allowed characters (alphanumeric, spaces, and specific safe symbols)
- Enforce maximum 1000 character limit on healthcheck commands
- Strip newlines and carriage returns to prevent command injection
- Change input field from textarea to text input in UI
- Add warning callout about prohibited shell operators
- Add comprehensive validation tests for both valid and malicious command patterns
- Add authorization checks ($this->authorize) for all read/write operations
- Use customApiValidator() instead of Validator::make() to match codebase patterns
- Add extra field rejection to prevent mass assignment
- Use Application::ownedByCurrentTeamAPI() for consistent query patterns
- Remove non-existent standalone_postgresql_id from hidden fields
- Add execution listing endpoints for both applications and services
- Add ScheduledTaskExecution OpenAPI schema
- Use $request->only() instead of $request->all() for safe updates
- Add ScheduledTaskFactory and feature tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Refactor server IP duplicate detection to use `first()` instead of `get()->count()`
- Add team-scoped validation to distinguish between same-team and cross-team IP conflicts
- Update error messages to clarify ownership: "already exists in your team" vs "in use by another team"
- Apply consistent validation logic across API, boarding, and server management flows
- Add comprehensive test suite for IP uniqueness enforcement across teams
This commit fixes two UX issues with environment variable bulk updates:
1. Comment Preservation (High Priority Bug):
- When bulk updating environment variables via Developer view, existing
manually-entered comments are now preserved when no inline comment is provided
- Only overwrites existing comments when an inline comment (#comment) is explicitly
provided in the pasted content
- Previously: pasting "KEY=value" would erase existing comment to null
- Now: pasting "KEY=value" preserves existing comment, "KEY=value #new" overwrites it
2. Save Notification (UX Improvement):
- "Save all Environment variables" button now always shows success notification
- Previously: only showed notification when changes were detected
- Now: provides feedback even when no changes were made
- Consistent with other save operations in the codebase
Changes:
- Modified updateOrCreateVariables() to only update comment field when inline comment
is provided (null check prevents overwriting existing comments)
- Modified handleBulkSubmit() to always dispatch success notification unless error occurred
- Added comprehensive test coverage for bulk update comment preservation scenarios
Tests:
- Added 4 new feature tests covering comment preservation edge cases
- All 22 existing unit tests for parseEnvFormatToArray pass
- Code formatted with Pint
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace permissive $guarded = [] with explicit $fillable array for better security and clarity. The fillable array includes all 13 fields that are legitimately mass-assignable:
- Core: key, value, comment
- Polymorphic relationship: resourceable_type, resourceable_id
- Boolean flags: is_preview, is_multiline, is_literal, is_runtime, is_buildtime, is_shown_once, is_shared
- Metadata: version, order
Also adds comprehensive test suite (EnvironmentVariableMassAssignmentTest) with 12 test cases covering:
- Mass assignment of all fillable fields
- Comment field edge cases (null, empty, long text)
- Value encryption verification
- Key mutation (trim and space replacement)
- Protection of auto-managed fields (id, uuid, timestamps)
- Update method compatibility
All tests passing (12 passed, 33 assertions).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add comment field to EnvironmentVariable model and database
- Update parseEnvFormatToArray to extract inline comments from env files
- Update Livewire components to handle comment field
- Add UI for displaying and editing comments
- Add tests for comment parsing functionality
- Add 429 response with Retry-After header for Hetzner server creation
- Create RateLimitException for proper rate limit error handling
- Rename cloud_provider_token_id to cloud_provider_token_uuid with deprecation
- Fix prices array schema in server-types endpoint with proper items definition
- Add explicit default: true to autogenerate_domain properties
- Add timeout and retry options to Docker install curl commands
- Fix race condition in deployment status update using atomic query
- Return the specific error from validateProviderToken() instead of
generic "Failed to validate token." message
- Update test to expect the actual error message
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add validateProviderToken() helper method to reduce code duplication
- Use request body only ($request->json()->all()) to avoid route parameter conflicts
- Add proper logging for token validation failures
- Add missing DB import to migration file
- Minor test formatting fix
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Add complete API support for Hetzner server provisioning, matching UI functionality:
Cloud Provider Token Management:
- POST /api/v1/cloud-tokens - Create and validate tokens
- GET /api/v1/cloud-tokens - List all tokens
- GET /api/v1/cloud-tokens/{uuid} - Get specific token
- PATCH /api/v1/cloud-tokens/{uuid} - Update token name
- DELETE /api/v1/cloud-tokens/{uuid} - Delete token
- POST /api/v1/cloud-tokens/{uuid}/validate - Validate token
Hetzner Resource Discovery:
- GET /api/v1/hetzner/locations - List datacenters
- GET /api/v1/hetzner/server-types - List server types
- GET /api/v1/hetzner/images - List OS images
- GET /api/v1/hetzner/ssh-keys - List SSH keys
Server Provisioning:
- POST /api/v1/servers/hetzner - Create server with full options
Features:
- Token validation against provider APIs before storage
- Smart SSH key management with MD5 fingerprint deduplication
- IPv4/IPv6 network configuration with preference logic
- Cloud-init script support with YAML validation
- Team-based isolation and security
- Comprehensive test coverage (40+ test cases)
- Complete documentation with curl examples and Yaak collection
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>