Commit graph

1424 commits

Author SHA1 Message Date
Andras Bacsai
336fa0c714 fix: critical privilege escalation in team invitation system
This commit addresses a critical security vulnerability where low-privileged
users (members) could invite high-privileged users (admins/owners) to teams,
allowing them to escalate their own privileges through password reset.

Root Causes Fixed:
1. TeamPolicy authorization checks were commented out, allowing all team
   members to manage invitations instead of just admins/owners
2. Missing role elevation checks in InviteLink component allowed members
   to invite users with higher privileges

Security Fixes:

1. app/Policies/TeamPolicy.php
   - Uncommented and enforced authorization checks for:
     * update() - Only admins/owners can update team settings
     * delete() - Only admins/owners can delete teams
     * manageMembers() - Only admins/owners can manage team members
     * viewAdmin() - Only admins/owners can view admin panel
     * manageInvitations() - Only admins/owners can manage invitations

2. app/Livewire/Team/InviteLink.php
   - Added explicit role elevation checks to prevent:
     * Members from inviting admins or owners
     * Admins from inviting owners (defense-in-depth)
   - Validates that inviter has sufficient privileges for target role

Test Coverage:

1. tests/Feature/TeamPolicyTest.php
   - 24 comprehensive tests covering all policy methods
   - Tests for owner, admin, member, and non-member access
   - Specific tests for the privilege escalation vulnerability

2. tests/Feature/TeamInvitationPrivilegeEscalationTest.php
   - 11 tests covering all role elevation scenarios
   - Tests member → admin/owner escalation (blocked)
   - Tests admin → owner escalation (blocked)
   - Tests valid invitation paths for each role

Impact:
- Prevents privilege escalation attacks
- Protects all Coolify instances from unauthorized access
- Enforces proper role hierarchy in team management

References:
- Identified by Aikido AI whitebox pentest service
- CVE: Pending assignment
- Severity: Critical

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 11:42:25 +02:00
Andras Bacsai
23c1184e86
Merge pull request #6880 from coollabsio/andrasbacsai/fix-new-image-quick-action
fix: 'new image' quick action not progressing to resource selection
2025-10-15 10:51:21 +02:00
Andras Bacsai
73837058c3
Merge pull request #6879 from coollabsio/fix-docker-image-digest-cleanup
fix: improve Docker image digest handling and add auto-parse feature
2025-10-15 10:49:30 +02:00
Andras Bacsai
66cff9d9b8 fix: 'new image' quick action not progressing to resource selection
Fixed three issues preventing the "new image" quick action from working:

1. Frontend matching logic wasn't checking the quickcommand field
   - Added check for item.quickcommand in the matching logic
   - Now "new image" matches docker-image via its quickcommand "(type: new image)"

2. Search query remained populated after triggering selection flow
   - Clear searchQuery in navigateToResourceCreation() to show selection UI
   - This switches the UI from creatable items list to server selection

3. Redirect wasn't using Livewire's redirect method
   - Changed from redirect()->route() to $this->redirect(route())
   - Ensures proper Livewire component redirect behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 10:49:07 +02:00
Andras Bacsai
20b4288916 fix: improve Docker image digest handling and add auto-parse feature
- Replace manual regex parsing with DockerImageParser in ApplicationsController
- Fix double-decoration bug where image names like nginx@sha256:hash would
  become nginx:hash@sha256 causing malformed references
- Add auto-parse feature in Livewire DockerImage component
- Users can now paste complete references like nginx:stable@sha256:abc123...
  and fields auto-populate
- Update UI placeholder with examples: nginx, docker.io/nginx:latest,
  ghcr.io/user/app:v1.2.3, nginx:stable@sha256:abc123...
- Add comprehensive unit tests for auto-parse functionality
- All tests passing (20 tests, 73 assertions)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 10:19:01 +02:00
Andras Bacsai
3e2f124c83 fix: use computed imageTag variable for digest-based Docker images
The code was computing $imageTag with the 'sha256-' prefix for digest-based
images but then using $parser->getTag() directly when creating the Application,
which bypassed the prefix logic entirely.

This fix ensures that digest-based Docker images preserve their 'sha256-' prefix
by using the computed $imageTag variable instead of calling $parser->getTag()
directly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 09:36:59 +02:00
Andras Bacsai
81455b1b5f
Merge pull request #6863 from YaRissi/hetzner/cpu_vendor
feat(hetzner): add CPU vendor information to server types in Hetzner integration
2025-10-15 09:03:12 +02:00
Andras Bacsai
e20327b9c4 fix: add authorization checks to database Livewire components
Added authorization checks to 11 database-related Livewire components
that were loading sensitive database configuration without verifying
user permissions.

Changes:
- Added authorize('view', $database) to all 8 database type General.php mount() methods
- Added authorization to Configuration.php before loading database
- Added authorization to BackupEdit.php before loading backup config
- Added authorization to Import.php before loading database resource

This prevents unauthorized users from accessing database credentials,
connection strings, and configuration details.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 17:33:42 +02:00
Andras Bacsai
74c70b431c fix: prevent TypeError in database General components with null server
Nullable server + guard to avoid TypeError/NPE. Don't terminate the app, terminate the bug.

Changes:
- Made Server property nullable (?Server $server = null) in all 8 database General components
- Added guard clause in mount() to check for null server before accessing it
- Displays user-friendly error message when destination server is not configured
- Prevents crashes in methods like isLogDrainEnabled() and sslCertificates()

Fixed components:
- Mariadb, Dragonfly, Clickhouse, Keydb
- Mysql, Mongodb, Redis, Postgresql

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 17:04:48 +02:00
Andras Bacsai
ce12c94709 fix: prevent duplicate services on image change and enable real-time UI refresh
This commit addresses two critical issues with Docker Compose service management:

## Issue 1: Duplicate Services Created on Image Change
When changing the image in a docker-compose file, the parser was creating new
ServiceApplication/ServiceDatabase records instead of updating existing ones.

**Root Cause**: The parsers used `firstOrCreate()` with `['name', 'image', 'service_id']`,
meaning any image change would create a new record.

**Fix**: Remove `image` from `firstOrCreate()` queries and update it separately after
finding or creating the service record.

**Changes**:
- `bootstrap/helpers/parsers.php` (serviceParser v3): Fixed in presave loop (lines 1188-1203)
  and main parsing loop (lines 1519-1539)
- `bootstrap/helpers/shared.php` (parseDockerComposeFile v2): Fixed null check logic
  (lines 1308-1348)

## Issue 2: UI Not Refreshing After Changes
When compose file or domain was modified, the Configuration component wasn't receiving
events to refresh its data, requiring manual page refresh to see updates.

**Root Cause**: The Configuration component wasn't listening for refresh events dispatched
by child components (StackForm, EditDomain).

**Fix**: Add event listeners and dispatchers to enable real-time UI updates.

**Changes**:
- `app/Livewire/Project/Service/Configuration.php`: Added listeners for `refreshServices`
  and `refresh` events (lines 36-37)
- `app/Livewire/Project/Service/EditDomain.php`: Added `refreshServices` dispatch (line 76)
- Note: `app/Livewire/Project/Service/StackForm.php` already had the dispatch

## Tests Added
- `tests/Unit/ServiceParserImageUpdateTest.php`: 4 tests verifying no duplicates created
- `tests/Unit/ServiceConfigurationRefreshTest.php`: 4 tests verifying event dispatching

All 8 new tests pass, and all existing unit tests continue to pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 10:12:36 +02:00
elmariss
9c79e2bfbc simplify the getCpuVendorInfo method 2025-10-13 22:41:13 +02:00
elmariss
ea584902ec feat: add CPU vendor information to server types in Hetzner integration 2025-10-13 22:21:35 +02:00
Andras Bacsai
777cdc91f0 fix: enable docker network connection for pgadmin service 2025-10-13 14:13:40 +02:00
Andras Bacsai
acc5dbe105
Merge branch 'next' into andrasbacsai/fix-livewire-field-reset 2025-10-13 10:38:02 +02:00
Andras Bacsai
a15ab54495 refactor: migrate database components from legacy model binding to explicit properties
- Remove global 'refresh' event listeners from all database General components
- Migrate Redis, MySQL, MariaDB, MongoDB, PostgreSQL, and KeyDB components to use explicit public properties instead of wire:model="database.field"
- Implement syncData() method in each component for manual data synchronization between properties and Eloquent models
- Update all validation rules, messages, and attributes to reference new property names
- Update Blade views to bind inputs to explicit properties (e.g., id="name" instead of id="database.name")
- Prepare codebase for disabling Livewire's legacy_model_binding configuration option

This refactoring resolves form field reset issues caused by global refresh events
and follows Livewire 3 best practices for component property management.
2025-10-13 10:01:17 +02:00
Andras Bacsai
de24489aa7 fix(onboarding): auto-select first SSH key for better UX
- Auto-select first SSH key when available instead of requiring explicit selection
- Remove disabled placeholder option from dropdown
- Prevents confusing error when user clicks "Use Selected Key" without changing dropdown
- Improves onboarding flow by having a sensible default selection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-12 18:54:12 +02:00
Andras Bacsai
04625591ea feat(onboarding): add Hetzner integration and fix navigation issues
- 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>
2025-10-12 18:52:45 +02:00
Andras Bacsai
1902ef886d
Merge branch 'next' into andrasbacsai/onboarding-redesign 2025-10-12 18:02:37 +02:00
Andras Bacsai
7a008c859a feat(onboarding): redesign user onboarding flow with modern UI/UX
- 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>
2025-10-12 17:59:37 +02:00
Andras Bacsai
b7af8cea5a
Merge pull request #6854 from coollabsio/andrasbacsai/terminal-datalist-dropdown
feat: replace terminal dropdown with searchable datalist component
2025-10-12 15:15:49 +02:00
Andras Bacsai
6297ac6c88 feat: replace terminal dropdown with searchable datalist component
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>
2025-10-12 14:57:45 +02:00
Andras Bacsai
635af44539
Merge pull request #6837 from coollabsio/andrasbacsai/custom-webhooks
feat: add custom webhook notification support
2025-10-12 10:57:47 +02:00
Andras Bacsai
7ad7247284 feat: add clear button for cloud-init script dropdown
Add a 'Clear' button next to the cloud-init script dropdown that:
- Resets the dropdown to default (placeholder option)
- Clears the cloud-init script textarea
- Clears the script name input
- Unchecks the 'save script' checkbox

Improves UX by allowing users to quickly reset cloud-init fields
without manually clearing each field.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 18:51:29 +02:00
Andras Bacsai
d93a13eeee feat: add YAML validation for cloud-init scripts
Add ValidCloudInitYaml validation rule to ensure cloud-init scripts
are properly formatted before saving. The validator supports:
- Cloud-config YAML (with or without #cloud-config header)
- Bash scripts (starting with #!)
- Empty/null values (optional field)

Uses Symfony YAML parser to validate YAML syntax and provides
detailed error messages when validation fails.

Added comprehensive unit tests covering:
- Valid cloud-config with/without header
- Valid bash scripts
- Invalid YAML syntax detection
- Complex multi-section cloud-config

Applied validation to:
- ByHetzner component (server creation)
- CloudInitScriptForm component (script management)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 13:56:55 +02:00
Andras Bacsai
a3cecff97b refactor: remove debug sleep from global search modal
Remove debug sleep(4) statement from openSearchModal method.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 13:49:19 +02:00
Andras Bacsai
ff889e658d refactor: improve cloud-init script management UI and cache control
- Add manual cache clearing command (search:clear) for testing
- Integrate cloud-init scripts into global search navigation
- Improve form UX by preventing field reset during edit operations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 13:47:26 +02:00
Andras Bacsai
ff69bf17cd feat: add cloud-init scripts to global search
Add cloud-init scripts to the global search navigation routes, making
them discoverable via the quick search (Cmd+K / Ctrl+K).

Changes:
- Added dedicated "Cloud-Init Scripts" navigation entry
- Searchable via: cloud-init, scripts, cloud init, cloudinit,
  initialization, startup, server setup
- Updated Security entry to include cloud-init in search terms
- Links to /security/cloud-init-scripts route

Users can now quickly navigate to cloud-init script management by
typing "cloud-init" or related terms in global search.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 13:33:55 +02:00
Andras Bacsai
6c5adce633 fix: improve cloud-init scripts UI styling and behavior
Fix multiple UI/UX issues with cloud-init scripts management:

1. Fix card styling - Remove purple box background, use simple border
   - Changed from .box class to inline flex/border styling
   - Matches cloud provider tokens styling pattern

2. Remove script preview section
   - Preview was taking too much space and looked cluttered
   - Users can edit to see full script content

3. Make edit modal full width
   - Added fullWidth attribute to x-modal-input component
   - Provides better editing experience for long scripts

4. Fix fields clearing after update
   - Fields were being reset even in edit mode
   - Now only reset fields when creating new script
   - Edit mode preserves values after save

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 13:30:44 +02:00
Andras Bacsai
5463f4d496 feat: add cloud-init scripts management UI in Security section
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>
2025-10-11 12:42:09 +02:00
Andras Bacsai
b31b080799 fix: reset cloud-init fields when closing server creation modal
Add cloud-init script fields to the resetSelection() method that's
called when the modal is closed. This ensures a clean slate when
reopening the "Connect a Hetzner Server" view.

Fields reset:
- cloud_init_script
- save_cloud_init_script
- cloud_init_script_name
- selected_cloud_init_script_id

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 12:37:12 +02:00
Andras Bacsai
6c0840d4e0 refactor: improve cloud-init script UX and remove description field
Changes:
1. Remove description field from cloud-init scripts
   - Updated migration to remove description column
   - Updated model to remove description from fillable array

2. Redesign script name input layout
   - Move script name input next to checkbox (always visible)
   - Remove conditional rendering - input always shown
   - Use placeholder instead of label for cleaner look

3. Fix dropdown type error
   - Replace wire:change event with wire:model.live
   - Use updatedSelectedCloudInitScriptId() lifecycle hook
   - Add "disabled" attribute to placeholder option
   - Properly handle empty string vs null in type casting

4. Improve validation
   - Require both script content AND name for saving
   - Remove description validation rule
   - Add selected_cloud_init_script_id validation

5. Auto-populate name when loading saved script
   - When user selects saved script, auto-fill the name field

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 11:16:28 +02:00
Andras Bacsai
7061eacfa5 feat: add cloud-init script support for Hetzner server creation
This commit adds the ability to use cloud-init scripts when creating Hetzner servers through the integration. Users can write custom scripts that will be executed during server initialization, and optionally save these scripts at the team level for future reuse.

Key features:
- Textarea field for entering cloud-init scripts (bash or cloud-config YAML)
- Checkbox to save scripts for later use at team level
- Dropdown to load previously saved scripts
- Scripts are encrypted in the database
- Full validation and authorization checks
- Comprehensive unit and feature tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 19:37:16 +02:00
Andras Bacsai
b48db997d6 feat: add pricing display to Hetzner server creation button
Display the monthly cost on the "Buy & Create Server" button
to give users clear visibility of the price before purchasing.

- Add computed property to calculate selected server's monthly price
- Update button text to show price dynamically (e.g., "€12.99/mo")
- Add tests for price formatting and edge cases
- Price updates reactively when user changes server type

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 18:50:44 +02:00
Andras Bacsai
7069236714 feat: add IPv4/IPv6 network configuration for Hetzner server creation
Add support for configuring IPv4 and IPv6 public network interfaces when creating servers through the Hetzner integration. Users can now enable or disable IPv4 and IPv6 independently, with both enabled by default.

Features:
- Added enable_ipv4 and enable_ipv6 checkboxes in the server creation form
- Both options are enabled by default as per Hetzner best practices
- IPv4 is preferred when both are enabled
- Fallback to IPv6 when only IPv6 is enabled
- Proper validation and error handling for network configuration
- Comprehensive test coverage for IP address selection logic

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 18:22:25 +02:00
Andras Bacsai
dc15bee980 feat: implement actual webhook delivery with Ray debugging
Added actual HTTP POST delivery for webhook notifications and comprehensive Ray debugging for development.

Changes:
- Updated Team model to implement SendsWebhook interface
- Added routeNotificationForWebhook() method to Team
- Enhanced SendWebhookJob with Ray logging for request/response
- Added Ray debugging to WebhookChannel for dispatch tracking
- Added Ray debugging to Webhook Livewire component

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 18:07:04 +02:00
Andras Bacsai
3c74620f36 feat: add modal support for creating private keys in server creation form and enhance UI for private key selection 2025-10-10 15:53:17 +02:00
Andras Bacsai
27879377a0 feat: add custom webhook notification support
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>
2025-10-10 15:37:00 +02:00
Andras Bacsai
ac3af8a882 feat: add support for selecting additional SSH keys from Hetzner in server creation form 2025-10-10 12:17:05 +02:00
Andras Bacsai
2e21d875af feat: implement ValidHostname validation rule and integrate it into server creation process 2025-10-10 11:03:13 +02:00
Andras Bacsai
bd88bbca5b fix: streamline proxy status handling in StartProxy and Navbar components 2025-10-10 10:41:58 +02:00
Andras Bacsai
5362952e2a fix: correct dispatch logic for Hetzner server status refresh in checkHetznerServerStatus method 2025-10-10 10:13:14 +02:00
Andras Bacsai
2bca22082c feat: add retry functionality for server validation process 2025-10-10 10:13:08 +02:00
Andras Bacsai
32b53d756a feat: enhance proxy status notifications with detailed messages for various states 2025-10-10 09:37:05 +02:00
Andras Bacsai
bbaef03602 fix: update Hetzner server status handling to prevent unnecessary database updates and improve UI responsiveness 2025-10-10 09:35:54 +02:00
Andras Bacsai
77dcabe51c fix: refresh server data before showing notification to ensure accurate proxy status 2025-10-10 09:35:49 +02:00
Andras Bacsai
f4e5c195fe refactor: replace direct SslCertificate queries with server relationship methods for consistency 2025-10-09 17:00:05 +02:00
Andras Bacsai
bf5c08d071 work work on hetzner integration 2025-10-09 16:54:13 +02:00
Andras Bacsai
704ddf2968 improved hetzner features 2025-10-09 12:53:57 +02:00
Andras Bacsai
215301fa8f basics of adding / removing hetzner servers 2025-10-09 10:41:29 +02:00
Andras Bacsai
c1bcc41546 init of cloud providers 2025-10-08 20:47:50 +02:00