-
Notifications
You must be signed in to change notification settings - Fork 13
feat: add Redis/Valkey database type support #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Add Redis/Valkey as a new database type with backup support using redis-cli --rdb for RDB snapshots. Automated restore is not supported due to Redis limitations — a manual restore instructions modal is shown instead.
📝 WalkthroughWalkthroughThis PR adds comprehensive Redis/Valkey support to the database backup system, including a new RedisDatabase service class, Redis-specific form validation and UI components, Redis backup workflow integration, explicit prevention of Redis restore operations, and corresponding Docker, CI, test infrastructure, and fixture updates. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/Integration/BackupRestoreTest.php (1)
3-8:⚠️ Potential issue | 🟡 MinorUpdate the file-level docblock to mention Redis.
The docblock states tests require MySQL and PostgreSQL containers, but Redis is now also required.
📝 Suggested docblock update
/** * Integration tests for backup and restore with real databases. * - * These tests require MySQL and PostgreSQL containers to be running. + * These tests require MySQL, PostgreSQL, and Redis containers to be running. * Run with: php artisan test --group=integration */
🤖 Fix all issues with AI agents
In `@app/Services/Backup/RestoreTask.php`:
- Around line 56-58: The current throw in RestoreTask.php uses
UnsupportedDatabaseTypeException with a full sentence which the exception
constructor will wrap into "Database type '...' is not supported" and garble the
message; fix by either throwing a RestoreException with your custom message
(e.g., when $targetServer->database_type === DatabaseType::REDIS throw new
RestoreException('Automated restore is not supported for Redis/Valkey. Please
restore manually.')) or by passing only the raw type identifier to
UnsupportedDatabaseTypeException (e.g., pass $targetServer->database_type) so
the constructor formats the message correctly.
In `@app/Services/DatabaseConnectionTester.php`:
- Around line 38-41: The error message for the SSH-tunneling guard incorrectly
says "SQLite" even when DatabaseType::REDIS is in the checked list; update the
return in the SSH check inside DatabaseConnectionTester (the block that checks
in_array($databaseType, [DatabaseType::SQLITE, DatabaseType::REDIS]) &&
$sshConfig !== null) to return a correct message that references the actual
disallowed types (e.g., "SSH tunneling is not supported for SQLite or Redis
databases") or construct the message dynamically using $databaseType, and keep
the call to self::error(...) unchanged.
In `@tests/Support/IntegrationTestHelpers.php`:
- Around line 146-155: The loadRedisTestData method currently runs redis-cli
without authentication and silences stderr, so authenticated Redis instances
fail silently; update loadRedisTestData to read the test Redis password from
configuration (testing.databases.redis.password), escape it (use escapeshellarg)
and, when non-empty, include the auth flag (e.g. -a <escapedPassword>) in both
exec calls, and stop redirecting stderr to /dev/null (or capture exec return
codes) so failures are visible; refer to loadRedisTestData, $server,
$fixtureFile and the config key testing.databases.redis.password when making the
change.
🧹 Nitpick comments (5)
app/Services/Backup/Databases/RedisDatabase.php (2)
23-81: Consider extracting the repeated command-building preamble.
getDumpCommandLine,getPingCommand, andgetInfoCommandall duplicate the sameredis-cli -h ... -p ... [auth] --no-auth-warningprefix. A small helper would reduce duplication and make adding future commands easier.♻️ Suggested refactor
+ /** + * Build a base redis-cli command with host, port, auth, and --no-auth-warning. + * + * `@param` array<string, mixed> $config + * `@return` array<string> + */ + private function buildBaseCommand(array $config): array + { + $parts = ['redis-cli']; + $parts[] = '-h '.escapeshellarg($config['host']); + $parts[] = '-p '.escapeshellarg((string) $config['port']); + $parts = array_merge($parts, $this->buildAuthFlags($config)); + $parts[] = '--no-auth-warning'; + + return $parts; + } + public function getDumpCommandLine(string $outputPath): string { - $parts = ['redis-cli']; - - $parts[] = '-h '.escapeshellarg($this->config['host']); - $parts[] = '-p '.escapeshellarg((string) $this->config['port']); - - $parts = array_merge($parts, $this->buildAuthFlags($this->config)); - - $parts[] = '--no-auth-warning'; + $parts = $this->buildBaseCommand($this->config); $parts[] = '--rdb '.escapeshellarg($outputPath); return implode(' ', $parts); }Apply the same pattern to
getPingCommandandgetInfoCommand.
48-61: Design note:getPingCommand/getInfoCommandtake$configas parameter whilegetDumpCommandLineuses instance$this->config.This inconsistency exists because the connection tester calls
getPingCommand/getInfoCommandwithoutsetConfig(), while the backup flow usessetConfig()+getDumpCommandLine(). It works correctly but is worth documenting in a PHPDoc note on the class to clarify the two usage patterns.app/Services/Backup/BackupTask.php (1)
52-56: DRY: extension-mapping logic is duplicated inrun()andgenerateFilename().The same
matchexpression for mappingDatabaseType→ file extension appears in both places. Consider extracting it to a private helper.♻️ Suggested refactor
+ private function getBaseExtension(DatabaseType $databaseType): string + { + return match ($databaseType) { + DatabaseType::SQLITE => 'db', + DatabaseType::REDIS => 'rdb', + default => 'sql', + }; + }Then replace both occurrences:
- $extension = match ($databaseServer->database_type) { - DatabaseType::SQLITE => 'db', - DatabaseType::REDIS => 'rdb', - default => 'sql', - }; + $extension = $this->getBaseExtension($databaseServer->database_type);Also applies to: 174-178
app/Livewire/Forms/DatabaseServerForm.php (1)
786-791: Consider excluding Redis from SSH config building, in addition to SQLite.Currently
$sshConfigis built wheneverssh_enabled && !isSqlite(). The UI hides SSH for Redis, so this is low-risk, but for defense-in-depth the condition should also exclude Redis — matching the guard inDatabaseConnectionTester::test().♻️ Suggested fix
- $sshConfig = $this->ssh_enabled && ! $this->isSqlite() + $sshConfig = $this->ssh_enabled && ! $this->isSqlite() && ! $this->isRedis() ? $this->buildSshConfigForTest() : null;resources/views/livewire/database-server/_form.blade.php (1)
101-103: SSH tunnel exclusion for Redis — is this intentional?Redis servers behind firewalls may also benefit from SSH tunneling. While the PR objectives mention hiding SSH tunnel fields for Redis, consider whether this is a permanent design decision or something that may need to be revisited. If intentional, a brief comment or documentation note explaining the rationale would help future maintainers.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (31)
.github/workflows/tests.ymlCLAUDE.mdapp/Enums/DatabaseType.phpapp/Livewire/DatabaseServer/Edit.phpapp/Livewire/DatabaseServer/Index.phpapp/Livewire/DatabaseServer/RestoreModal.phpapp/Livewire/Forms/DatabaseServerForm.phpapp/Services/Backup/BackupJobFactory.phpapp/Services/Backup/BackupTask.phpapp/Services/Backup/DatabaseListService.phpapp/Services/Backup/Databases/RedisDatabase.phpapp/Services/Backup/RestoreTask.phpapp/Services/DatabaseConnectionTester.phpconfig/testing.phpdatabase/factories/DatabaseServerFactory.phpdatabase/seeders/DatabaseSeeder.phpdocker-compose.ymldocker/php/Dockerfileresources/views/livewire/database-server/_form.blade.phpresources/views/livewire/database-server/index.blade.phpresources/views/livewire/database-server/restore-modal.blade.phptests/Feature/DatabaseServer/CreateTest.phptests/Feature/DatabaseServer/EditTest.phptests/Feature/DatabaseServer/RestoreModalTest.phptests/Feature/Services/Backup/BackupTaskTest.phptests/Feature/Services/Backup/RestoreTaskTest.phptests/Integration/BackupRestoreTest.phptests/Integration/DatabaseConnectionTesterTest.phptests/Integration/fixtures/redis-init.txttests/Pest.phptests/Support/IntegrationTestHelpers.php
🧰 Additional context used
📓 Path-based instructions (13)
**/*.php
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.php: All PHP commands (php, composer, vendor/bin/*) MUST be run through Docker usingdocker compose exec --user application -T app <command>. Never run these commands directly on the host.
Always use explicit return type declarations for methods and functions
Use PHP 8 constructor property promotion in__construct()methods with explicit parameter types
Do not allow empty__construct()methods with zero parameters unless the constructor is private
Always use curly braces for control structures, even for single-line bodies
Use appropriate PHP type hints for method parameters
Prefer PHPDoc blocks over inline comments; never use comments within the code itself unless the logic is exceptionally complex
Keys in Enums should typically be TitleCase (e.g., FavoritePerson, BestLake, Monthly)
Always use proper Eloquent relationship methods with return type hints; prefer relationship methods over raw queries or manual joins
Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum) rather than custom implementations
AvoidDB::; preferModel::query()and leverage Laravel's ORM capabilities rather than bypassing them with raw queries
Files:
tests/Integration/DatabaseConnectionTesterTest.phpapp/Services/DatabaseConnectionTester.phptests/Feature/Services/Backup/RestoreTaskTest.phpapp/Services/Backup/RestoreTask.phpapp/Services/Backup/Databases/RedisDatabase.phpconfig/testing.phpdatabase/seeders/DatabaseSeeder.phptests/Feature/DatabaseServer/CreateTest.phptests/Pest.phpresources/views/livewire/database-server/_form.blade.phpresources/views/livewire/database-server/restore-modal.blade.phpapp/Services/Backup/BackupTask.phpapp/Livewire/DatabaseServer/Edit.phpapp/Livewire/Forms/DatabaseServerForm.phptests/Feature/DatabaseServer/RestoreModalTest.phptests/Integration/BackupRestoreTest.phpdatabase/factories/DatabaseServerFactory.phpapp/Enums/DatabaseType.phptests/Support/IntegrationTestHelpers.phptests/Feature/DatabaseServer/EditTest.phpresources/views/livewire/database-server/index.blade.phpapp/Services/Backup/BackupJobFactory.phpapp/Services/Backup/DatabaseListService.phpapp/Livewire/DatabaseServer/RestoreModal.phptests/Feature/Services/Backup/BackupTaskTest.phpapp/Livewire/DatabaseServer/Index.php
tests/**/*.php
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.php: Use factories for creating models in tests via theModelFactory::create()pattern; check for custom states before manually setting up models
Use Faker methods such as$this->faker->word()orfake()->randomDigit()for generating test data
Files:
tests/Integration/DatabaseConnectionTesterTest.phptests/Feature/Services/Backup/RestoreTaskTest.phptests/Feature/DatabaseServer/CreateTest.phptests/Pest.phptests/Feature/DatabaseServer/RestoreModalTest.phptests/Integration/BackupRestoreTest.phptests/Support/IntegrationTestHelpers.phptests/Feature/DatabaseServer/EditTest.phptests/Feature/Services/Backup/BackupTaskTest.php
app/Services/**/*.php
📄 CodeRabbit inference engine (CLAUDE.md)
Services should be injected into component constructors using PHP 8 constructor property promotion
Files:
app/Services/DatabaseConnectionTester.phpapp/Services/Backup/RestoreTask.phpapp/Services/Backup/Databases/RedisDatabase.phpapp/Services/Backup/BackupTask.phpapp/Services/Backup/BackupJobFactory.phpapp/Services/Backup/DatabaseListService.php
tests/Feature/**/*.php
📄 CodeRabbit inference engine (CLAUDE.md)
Write a new test or update an existing test for every code change; run affected tests to ensure they pass
Files:
tests/Feature/Services/Backup/RestoreTaskTest.phptests/Feature/DatabaseServer/CreateTest.phptests/Feature/DatabaseServer/RestoreModalTest.phptests/Feature/DatabaseServer/EditTest.phptests/Feature/Services/Backup/BackupTaskTest.php
app/Services/Backup/Databases/*.php
📄 CodeRabbit inference engine (CLAUDE.md)
Database handler classes should implement the
DatabaseInterfacefor backup/restore operations
Files:
app/Services/Backup/Databases/RedisDatabase.php
config/*.php
📄 CodeRabbit inference engine (CLAUDE.md)
Use environment variables only in configuration files; never use the
env()function directly outside of config files - always useconfig('key.name')
Files:
config/testing.php
resources/views/livewire/**/*.blade.php
📄 CodeRabbit inference engine (CLAUDE.md)
Blade files for Livewire components should contain only view markup; all PHP logic must be in component classes
Files:
resources/views/livewire/database-server/_form.blade.phpresources/views/livewire/database-server/restore-modal.blade.phpresources/views/livewire/database-server/index.blade.php
resources/views/**/*.blade.php
📄 CodeRabbit inference engine (CLAUDE.md)
resources/views/**/*.blade.php: Use Mary UI components prefixed withx-(e.g.,<x-button>,<x-input>,<x-card>)
Use Heroicons for icons with the notationicon="o-user"for outline icons oricon="s-user"for solid icons
Use Mary UI select components with:optionsprop in format[['id' => 'value', 'name' => 'Label']]
Use Mary UI alert classes with formatclass="alert-success",class="alert-error", etc.
Use Mary UI tables with<table class="table-default">with custom styling
Files:
resources/views/livewire/database-server/_form.blade.phpresources/views/livewire/database-server/restore-modal.blade.phpresources/views/livewire/database-server/index.blade.php
resources/**/*.blade.php
📄 CodeRabbit inference engine (CLAUDE.md)
Use Tailwind CSS v4 utility classes for styling (flex, grid, responsive design, dark mode with
prefers-color-scheme)
Files:
resources/views/livewire/database-server/_form.blade.phpresources/views/livewire/database-server/restore-modal.blade.phpresources/views/livewire/database-server/index.blade.php
app/Livewire/**/*.php
📄 CodeRabbit inference engine (CLAUDE.md)
app/Livewire/**/*.php: Public properties in Livewire components are automatically bound to views
Use#[Validate]attributes or Form objects for validation in Livewire components
Call$this->validate()before processing data in Livewire components
UseSession::flash()for one-time messages in Livewire components, displayed via@if (session('success'))in Blade
Return$this->redirect()withnavigate: truefor SPA-like navigation in Livewire components
Use boolean properties withwire:modelfor controlling modal visibility (e.g.,$showDeleteModal)
Files:
app/Livewire/DatabaseServer/Edit.phpapp/Livewire/Forms/DatabaseServerForm.phpapp/Livewire/DatabaseServer/RestoreModal.phpapp/Livewire/DatabaseServer/Index.php
app/Livewire/Forms/*.php
📄 CodeRabbit inference engine (CLAUDE.md)
Form classes should encapsulate validation logic with a
rules()method and include validation attributes
Files:
app/Livewire/Forms/DatabaseServerForm.php
app/Enums/*.php
📄 CodeRabbit inference engine (CLAUDE.md)
Add useful array shape type definitions in PHPDoc blocks for Enum methods that return arrays
Files:
app/Enums/DatabaseType.php
app/Enums/DatabaseType.php
📄 CodeRabbit inference engine (CLAUDE.md)
When adding a new database type, include enum case, label, default port, and DSN format in
buildDsn()method
Files:
app/Enums/DatabaseType.php
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to app/Services/Backup/Databases/*.php : Database handler classes should implement the `DatabaseInterface` for backup/restore operations
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to app/Services/Backup/Databases/*.php : Database handler classes should implement the `DatabaseInterface` for backup/restore operations
Applied to files:
app/Services/DatabaseConnectionTester.phptests/Feature/Services/Backup/RestoreTaskTest.phpapp/Services/Backup/RestoreTask.phpapp/Services/Backup/Databases/RedisDatabase.phpdatabase/seeders/DatabaseSeeder.phpapp/Services/Backup/BackupTask.phpCLAUDE.mdapp/Services/Backup/BackupJobFactory.phpapp/Services/Backup/DatabaseListService.phpapp/Livewire/DatabaseServer/RestoreModal.phptests/Feature/Services/Backup/BackupTaskTest.phpapp/Livewire/DatabaseServer/Index.php
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to app/Services/Backup/Filesystems/*.php : Filesystem classes should implement the `FilesystemInterface` for volume storage operations
Applied to files:
app/Services/DatabaseConnectionTester.phpapp/Services/Backup/Databases/RedisDatabase.phpapp/Services/Backup/BackupTask.phpCLAUDE.mdtests/Feature/Services/Backup/BackupTaskTest.php
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to app/Jobs/*.php : Queue jobs like `ProcessBackupJob` and `ProcessRestoreJob` should wrap service classes and configure retries/timeout appropriately
Applied to files:
app/Services/DatabaseConnectionTester.phpapp/Services/Backup/BackupTask.phpCLAUDE.mdtests/Feature/Services/Backup/BackupTaskTest.php
📚 Learning: 2026-02-07T23:36:48.880Z
Learnt from: David-Crty
Repo: David-Crty/databasement PR: 76
File: app/Jobs/ProcessRestoreJob.php:32-34
Timestamp: 2026-02-07T23:36:48.880Z
Learning: Restore operations in ProcessRestoreJob are idempotent (they drop and recreate the database regardless), so retrying failed restores is safe and ProcessRestoreJob intentionally shares the backup.job_tries configuration with ProcessBackupJob.
Applied to files:
app/Services/Backup/RestoreTask.phpCLAUDE.md
📚 Learning: 2026-01-30T22:27:46.107Z
Learnt from: David-Crty
Repo: David-Crty/databasement PR: 61
File: resources/views/livewire/volume/connectors/s3-config.blade.php:1-13
Timestamp: 2026-01-30T22:27:46.107Z
Learning: In Blade template files (any .blade.php) within the databasement project, allow using alert-info for informational content inside <x-alert> components. The guideline that permits alert-success and alert-error does not exclude using alert-info for informational purposes. Apply this consistently to all Blade components that render alerts; ensure semantic usage and accessibility.
Applied to files:
resources/views/livewire/database-server/_form.blade.phpresources/views/livewire/database-server/restore-modal.blade.phpresources/views/livewire/database-server/index.blade.php
📚 Learning: 2026-02-06T10:34:43.585Z
Learnt from: David-Crty
Repo: David-Crty/databasement PR: 75
File: resources/views/livewire/backup-job/_filters.blade.php:36-40
Timestamp: 2026-02-06T10:34:43.585Z
Learning: In Blade template files, when creating compact inline filter controls, prefer using native <input type="checkbox"> elements with daisyUI classes (e.g., checkbox checkbox-warning checkbox-xs) over the Mary UI <x-checkbox> component. The <x-checkbox> component adds wrapper markup (e.g., <div><fieldset><label> with gap-3) that can break tight inline flex layouts. Use the native input approach for compact inline controls, but reserve <x-checkbox> for form fields that require labels, hints, and errors.
Applied to files:
resources/views/livewire/database-server/_form.blade.phpresources/views/livewire/database-server/restore-modal.blade.phpresources/views/livewire/database-server/index.blade.php
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to tests/**/*.php : Use factories for creating models in tests via the `ModelFactory::create()` pattern; check for custom states before manually setting up models
Applied to files:
tests/Feature/DatabaseServer/RestoreModalTest.php
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: The Backup & Restore workflow operates asynchronously via the queue: backups dispatch to queue → queue worker executes BackupTask → creates Snapshot with status; restores dispatch ProcessRestoreJob → queue worker executes RestoreTask
Applied to files:
tests/Integration/BackupRestoreTest.phpCLAUDE.mdtests/Feature/Services/Backup/BackupTaskTest.php
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to app/Enums/DatabaseType.php : When adding a new database type, include enum case, label, default port, and DSN format in `buildDsn()` method
Applied to files:
app/Enums/DatabaseType.phpCLAUDE.mdapp/Livewire/DatabaseServer/RestoreModal.phpapp/Livewire/DatabaseServer/Index.php
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Use the Laravel Boost `search-docs` tool for version-specific documentation before making code changes to ensure correct approach
Applied to files:
CLAUDE.md
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to **/*.php : All PHP commands (php, composer, vendor/bin/*) MUST be run through Docker using `docker compose exec --user application -T app <command>`. Never run these commands directly on the host.
Applied to files:
CLAUDE.md
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to app/Enums/VolumeType.php : VolumeType enum should implement `label()`, `icon()`, `sensitiveFields()`, and `configSummary()` methods
Applied to files:
CLAUDE.md
📚 Learning: 2026-02-01T11:15:00.924Z
Learnt from: David-Crty
Repo: David-Crty/databasement PR: 64
File: docs/docs/self-hosting/configuration/backup.md:141-144
Timestamp: 2026-02-01T11:15:00.924Z
Learning: Self-hosting documentation (in `docs/docs/self-hosting/`) should use plain `php artisan` commands since the deployment environment is unknown. The Docker command requirement (`docker compose exec --user application -T app`) applies only to local development, not deployment documentation.
Applied to files:
CLAUDE.md
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to app/Livewire/**/*.php : Use boolean properties with `wire:model` for controlling modal visibility (e.g., `$showDeleteModal`)
Applied to files:
resources/views/livewire/database-server/index.blade.phpapp/Livewire/DatabaseServer/RestoreModal.phpapp/Livewire/DatabaseServer/Index.php
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to app/Livewire/**/*.php : Use `#[Validate]` attributes or Form objects for validation in Livewire components
Applied to files:
app/Livewire/DatabaseServer/RestoreModal.php
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to app/Livewire/**/*.php : Call `$this->validate()` before processing data in Livewire components
Applied to files:
app/Livewire/DatabaseServer/RestoreModal.php
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to app/Livewire/Forms/*.php : Form classes should encapsulate validation logic with a `rules()` method and include validation attributes
Applied to files:
app/Livewire/DatabaseServer/RestoreModal.php
📚 Learning: 2026-02-09T21:29:11.506Z
Learnt from: CR
Repo: David-Crty/databasement PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-09T21:29:11.506Z
Learning: Applies to app/Livewire/**/*.php : Public properties in Livewire components are automatically bound to views
Applied to files:
app/Livewire/DatabaseServer/RestoreModal.php
🧬 Code graph analysis (13)
app/Services/DatabaseConnectionTester.php (1)
app/Services/Backup/Databases/RedisDatabase.php (3)
RedisDatabase(5-104)getPingCommand(48-61)getInfoCommand(68-81)
tests/Feature/Services/Backup/RestoreTaskTest.php (2)
app/Exceptions/Backup/UnsupportedDatabaseTypeException.php (1)
UnsupportedDatabaseTypeException(5-11)app/Services/Backup/RestoreTask.php (1)
run(47-146)
app/Services/Backup/RestoreTask.php (2)
app/Models/Restore.php (1)
targetServer(70-73)app/Exceptions/Backup/UnsupportedDatabaseTypeException.php (1)
UnsupportedDatabaseTypeException(5-11)
database/seeders/DatabaseSeeder.php (2)
database/factories/DatabaseServerFactory.php (2)
redis(55-67)sqlite(38-50)app/Models/DatabaseServer.php (1)
DatabaseServer(60-218)
tests/Feature/DatabaseServer/CreateTest.php (1)
app/Services/AppConfigService.php (1)
set(66-86)
app/Livewire/DatabaseServer/Edit.php (1)
app/Livewire/Forms/DatabaseServerForm.php (2)
isSqlite(346-349)isRedis(354-357)
tests/Feature/DatabaseServer/RestoreModalTest.php (2)
app/Models/DatabaseServer.php (1)
DatabaseServer(60-218)app/Services/DatabaseConnectionTester.php (1)
test(30-93)
tests/Integration/BackupRestoreTest.php (6)
app/Models/Backup.php (3)
volume(77-80)databaseServer(69-72)snapshots(93-96)app/Models/Snapshot.php (4)
volume(209-212)databaseServer(193-196)backup(201-204)job(225-228)tests/Support/IntegrationTestHelpers.php (2)
IntegrationTestHelpers(14-245)loadRedisTestData(146-155)app/Services/Backup/BackupJobFactory.php (1)
createSnapshots(30-73)app/Services/Backup/BackupTask.php (1)
run(43-135)app/Services/Backup/Filesystems/FilesystemProvider.php (1)
getForVolume(34-44)
tests/Support/IntegrationTestHelpers.php (1)
app/Models/DatabaseServer.php (1)
DatabaseServer(60-218)
app/Services/Backup/BackupJobFactory.php (2)
app/Models/DatabaseServer.php (1)
snapshots(120-123)tests/Feature/Console/CleanupExpiredSnapshotsTest.php (1)
createSnapshot(8-27)
app/Services/Backup/DatabaseListService.php (2)
app/Models/Backup.php (1)
databaseServer(69-72)app/Models/Snapshot.php (1)
databaseServer(193-196)
app/Livewire/DatabaseServer/RestoreModal.php (2)
app/Livewire/Forms/DatabaseServerForm.php (1)
isSqlite(346-349)app/Models/Restore.php (1)
targetServer(70-73)
tests/Feature/Services/Backup/BackupTaskTest.php (2)
app/Services/Backup/Databases/RedisDatabase.php (1)
RedisDatabase(5-104)tests/Support/TestShellProcessor.php (1)
getCommands(104-107)
🪛 LanguageTool
CLAUDE.md
[grammar] ~332-~332: Ensure spelling is correct
Context: ... php (FrankenPHP), queue worker, mysql, postgres, redis === ...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🪛 PHPMD (2.15.0)
app/Services/Backup/Databases/RedisDatabase.php
[warning] 38-38: Avoid unused parameters such as '$inputPath'. (undefined)
(UnusedFormalParameter)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-base-image
🔇 Additional comments (41)
docker/php/Dockerfile (1)
39-40: Consider installing onlyredisCLI tools to reduce image size.The
redisAlpine package includes both the server and CLI. If onlyredis-cliis needed (forredis-cli --rdbandredis-cli PING), this adds unnecessary bloat. However, Alpine doesn't split these into separate packages, so this is acceptable as-is.database/seeders/DatabaseSeeder.php (1)
86-98: LGTM!Redis server seeding and inclusion in the backup configuration loop are consistent with the existing patterns for MySQL, PostgreSQL, and SQLite.
config/testing.php (1)
34-38: LGTM!Redis test configuration follows the established pattern and correctly uses
env()within the config file. As per coding guidelines,env()usage is appropriate here since this is a config file.docker-compose.yml (1)
85-96: LGTM!Redis service definition follows the established patterns for other database services, with proper healthcheck configuration and persistent volume.
.github/workflows/tests.yml (2)
36-44: LGTM!Redis CI service configuration is consistent with the existing MySQL and PostgreSQL service patterns, including proper health checks.
61-61: LGTM!Adding
redis-toolsprovidesredis-clifor CI test runs.app/Enums/DatabaseType.php (1)
12-12: LGTM!New
REDISenum case is correctly added with all required mappings: label, default port, and DSN/PDO guards. This follows the coding guideline for adding new database types. Based on learnings: "When adding a new database type, include enum case, label, default port, and DSN format inbuildDsn()method."app/Services/Backup/DatabaseListService.php (1)
38-41: LGTM!Clean early-return guard that correctly prevents Redis from reaching the PDO-based listing logic. Returning
['all']is semantically appropriate since Redis RDB dumps are whole-instance snapshots.app/Livewire/DatabaseServer/RestoreModal.php (2)
130-134: LGTM — correctly scopes app-database check to MySQL/PostgreSQL only.Excluding Redis (and SQLite) from the
isAppDatabase()check is correct since neither can serve as the Laravel application database.
168-168: Good improvement: direct enum comparison instead of string-based check.Using
DatabaseType::SQLITEenum comparison is more type-safe than the previous->valuestring comparison.database/factories/DatabaseServerFactory.php (1)
52-67: LGTM — Redis factory follows established pattern.Consistent with the
sqlite()factory method and correctly sets Redis-specific defaults (port: 6379,backup_all_databases: true,database_names: null).resources/views/livewire/database-server/restore-modal.blade.php (1)
1-169: LGTM — template restructuring with no behavioral changes.Proper use of Mary UI components, Heroicons, Tailwind utilities, and Livewire bindings throughout. Step flow and loading states are well-handled.
app/Services/Backup/Databases/RedisDatabase.php (1)
38-41: Static analysis hint about unused$inputPathis a false positive — parameter is required by the interface contract.The
@SuppressWarningsor a brief PHPDoc note would silence the PHPMD warning, but this is entirely optional.app/Services/Backup/BackupJobFactory.php (1)
45-50: LGTM — Redis snapshot creation follows the established SQLite pattern.Single snapshot with
databaseName: 'all'correctly represents the whole-instance RDB dump. Clean early return avoids the per-database listing logic.app/Services/DatabaseConnectionTester.php (1)
252-295: LGTM — Redis connection test is well-structured.The two-step approach (PING for connectivity, then non-critical INFO for metadata) with graceful degradation on the info step is solid. The
explode(':', $line, 2)correctly handles values that may contain colons.app/Services/Backup/BackupTask.php (2)
23-31: LGTM — Redis injected cleanly via constructor property promotion.Follows the established pattern for database handler dependencies.
146-151: LGTM — Redis dump command wired correctly into the backup flow.Redis goes through
configureDatabaseInterface→getDumpCommandLine, consistent with the MySQL/PostgreSQL path.app/Livewire/Forms/DatabaseServerForm.php (4)
351-357: LGTM —isRedis()follows the establishedisSqlite()pattern.
525-538: LGTM — Redis validation rules are appropriate.Username is correctly
nullable(Redis 6+ ACL is optional), and SSH/database-selection fields are correctly omitted since Redis doesn't use them.
568-572: LGTM — Redis forced tobackup_all_databases: truein both store and update paths.Consistent enforcement prevents misconfiguration.
Also applies to: 605-609
756-760: LGTM — Redis connection test validates only host and port.Correctly reflects that Redis authentication is optional and doesn't require a username.
app/Livewire/DatabaseServer/Index.php (1)
104-119: LGTM! Clean implementation of the Redis restore flow. SettingrestoreIdbefore the Redis check is correct since the modal template references it for the "View Backup Snapshots" link.app/Livewire/DatabaseServer/Edit.php (1)
65-70: LGTM! Correctly skips database loading for Redis, consistent with the SQLite pattern — Redis doesn't have named databases to enumerate.resources/views/livewire/database-server/_form.blade.php (1)
85-98: LGTM! Good UX — making username and password optional for Redis with a clear placeholder hint about ACL-enabled servers.resources/views/livewire/database-server/index.blade.php (1)
229-263: LGTM! Well-structured Redis restore modal with clear step-by-step instructions. Good use ofalert-infofor the informational context and the conditional "View Backup Snapshots" link.tests/Pest.php (1)
114-139: LGTM! Redis dataset entries follow the established patterns and use the correct default port (6379).tests/Feature/Services/Backup/RestoreTaskTest.php (1)
522-557: LGTM! Clean test that validates the Redis restore guard. Correctly expectsUnsupportedDatabaseTypeExceptionwithout setting up download/restore expectations since the exception fires before those code paths.tests/Integration/fixtures/redis-init.txt (1)
1-7: LGTM! Good coverage of Redis data types (strings with JSON, hashes, lists) for integration testing.tests/Feature/DatabaseServer/CreateTest.php (1)
29-32: LGTM! Correctly sets only host and port for Redis, omitting credentials and database names — consistent with the form making those fields optional for Redis.tests/Feature/DatabaseServer/EditTest.php (2)
27-30: LGTM — Redis branch in server creation is clean.The Redis path correctly sets
host,port, andbackup_all_databaseswithout username/password, consistent with how Redis is modeled elsewhere.
59-64: LGTM — Redis edit assertions follow the established pattern.Asserts
hostandportare loaded, then updatesnameandhost, matching the structure of the other database type branches.tests/Feature/DatabaseServer/RestoreModalTest.php (1)
191-200: LGTM — Redis restore test correctly validates the manual instructions modal.Good separation: Redis is excluded from the parameterized
RestoreModaltests and instead gets its own test usingIndex::classwith theconfirmRestoreaction, matching the architectural decision that Redis doesn't support automated restore.tests/Integration/DatabaseConnectionTesterTest.php (2)
14-46: LGTM — Redis integration into connection success test is well-structured.Clean separation of concerns: Redis skips both data setup and cleanup, and the
?? nullon Line 33 gracefully handles types that don't define adatabasekey.
133-145: LGTM — Redis failure test is a good complement to the success path.Tests that the connection tester properly reports failure when Redis is unreachable on the wrong port.
tests/Integration/BackupRestoreTest.php (1)
183-209: LGTM — Redis backup workflow test is thorough and well-aligned with the architecture.Assertions correctly validate the Redis-specific snapshot behavior:
database_nameis'all', filename ends with.rdb.gz, and there's no restore step since it's unsupported. TheafterEachcleanup safely skips Redis sincerestoredDatabaseNameis never set.tests/Feature/Services/Backup/BackupTaskTest.php (2)
415-452: LGTM — Redis backup workflow test validates the full command pipeline.Correctly verifies both the
redis-cli --rdbdump command and the gzip compression step, with proper snapshot metadata assertions.
454-482: LGTM — Redis authentication test ensures credentials are properly passed to redis-cli.Validates the
--userand-aflags are present in the correct order, matching theRedisDatabase.buildAuthFlags()implementation.tests/Support/IntegrationTestHelpers.php (2)
62-69: LGTM — Redis config entry follows the established pattern.Correctly reads from
config('testing.databases.redis.*')with appropriate defaults.
124-141:createRedisDatabaseServercould reusecreateDatabaseServerbut the dedicated method is justified.Redis requires
backup_all_databases => trueand omitsdatabase_names, making a separate factory method cleaner than conditionalizing the generic one. Good call.CLAUDE.md (2)
234-237: Good addition — Architecture notes for non-PDO types.Documenting that types like Redis must throw in
buildDsn()/createPdo()and get special handling in several services is valuable institutional knowledge for future contributors.
206-232: LGTM — Comprehensive checklist for adding new database types.The updated file list covers all the touchpoints needed for a new database type, which will serve as a good template for future additions.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| if ($targetServer->database_type === DatabaseType::REDIS) { | ||
| throw new UnsupportedDatabaseTypeException('Automated restore is not supported for Redis/Valkey. Please restore manually.'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Exception message will be garbled due to constructor formatting.
UnsupportedDatabaseTypeException wraps its argument as "Database type '{$databaseType}' is not supported" (see app/Exceptions/Backup/UnsupportedDatabaseTypeException.php). Passing the full sentence here produces:
Database type 'Automated restore is not supported for Redis/Valkey. Please restore manually.' is not supported
Either pass just the type identifier and let the constructor format it, or use a different exception (e.g., RestoreException) for the custom message.
Proposed fix (option A: use RestoreException with custom message)
- if ($targetServer->database_type === DatabaseType::REDIS) {
- throw new UnsupportedDatabaseTypeException('Automated restore is not supported for Redis/Valkey. Please restore manually.');
- }
+ if ($targetServer->database_type === DatabaseType::REDIS) {
+ throw new RestoreException('Automated restore is not supported for Redis/Valkey. Please restore manually.');
+ }Proposed fix (option B: pass just the type value)
- throw new UnsupportedDatabaseTypeException('Automated restore is not supported for Redis/Valkey. Please restore manually.');
+ throw new UnsupportedDatabaseTypeException($targetServer->database_type->value);🤖 Prompt for AI Agents
In `@app/Services/Backup/RestoreTask.php` around lines 56 - 58, The current throw
in RestoreTask.php uses UnsupportedDatabaseTypeException with a full sentence
which the exception constructor will wrap into "Database type '...' is not
supported" and garble the message; fix by either throwing a RestoreException
with your custom message (e.g., when $targetServer->database_type ===
DatabaseType::REDIS throw new RestoreException('Automated restore is not
supported for Redis/Valkey. Please restore manually.')) or by passing only the
raw type identifier to UnsupportedDatabaseTypeException (e.g., pass
$targetServer->database_type) so the constructor formats the message correctly.
| // SSH tunneling is not supported for local-only database types | ||
| if (in_array($databaseType, [DatabaseType::SQLITE, DatabaseType::REDIS]) && $sshConfig !== null) { | ||
| return self::error('SSH tunneling is not supported for SQLite databases.'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Error message is incorrect when Redis is selected with SSH tunneling.
The condition now includes DatabaseType::REDIS but the error message still references only SQLite. A Redis user encountering this would see a confusing message.
🐛 Proposed fix
// SSH tunneling is not supported for local-only database types
if (in_array($databaseType, [DatabaseType::SQLITE, DatabaseType::REDIS]) && $sshConfig !== null) {
- return self::error('SSH tunneling is not supported for SQLite databases.');
+ return self::error("SSH tunneling is not supported for {$databaseType->label()} databases.");
}🤖 Prompt for AI Agents
In `@app/Services/DatabaseConnectionTester.php` around lines 38 - 41, The error
message for the SSH-tunneling guard incorrectly says "SQLite" even when
DatabaseType::REDIS is in the checked list; update the return in the SSH check
inside DatabaseConnectionTester (the block that checks in_array($databaseType,
[DatabaseType::SQLITE, DatabaseType::REDIS]) && $sshConfig !== null) to return a
correct message that references the actual disallowed types (e.g., "SSH
tunneling is not supported for SQLite or Redis databases") or construct the
message dynamically using $databaseType, and keep the call to self::error(...)
unchanged.
| public static function loadRedisTestData(DatabaseServer $server): void | ||
| { | ||
| $fixtureFile = __DIR__.'/../Integration/fixtures/redis-init.txt'; | ||
| $host = escapeshellarg($server->host); | ||
| $port = escapeshellarg((string) $server->port); | ||
|
|
||
| // Flush existing data and load fixture | ||
| exec("redis-cli -h {$host} -p {$port} FLUSHALL 2>/dev/null"); | ||
| exec("redis-cli -h {$host} -p {$port} < {$fixtureFile} 2>/dev/null"); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loadRedisTestData ignores authentication credentials from config.
If testing.databases.redis.password is configured (e.g., in CI or a secured test environment), these redis-cli commands will fail silently since stderr is suppressed. Consider passing auth flags when a password is set.
🛡️ Suggested fix
public static function loadRedisTestData(DatabaseServer $server): void
{
$fixtureFile = __DIR__.'/../Integration/fixtures/redis-init.txt';
$host = escapeshellarg($server->host);
$port = escapeshellarg((string) $server->port);
+ $password = $server->getDecryptedPassword();
+ $authFlags = ! empty($password) ? '-a ' . escapeshellarg($password) . ' --no-auth-warning ' : '';
// Flush existing data and load fixture
- exec("redis-cli -h {$host} -p {$port} FLUSHALL 2>/dev/null");
- exec("redis-cli -h {$host} -p {$port} < {$fixtureFile} 2>/dev/null");
+ exec("redis-cli -h {$host} -p {$port} {$authFlags}FLUSHALL 2>/dev/null");
+ exec("redis-cli -h {$host} -p {$port} {$authFlags}< {$fixtureFile} 2>/dev/null");
}🤖 Prompt for AI Agents
In `@tests/Support/IntegrationTestHelpers.php` around lines 146 - 155, The
loadRedisTestData method currently runs redis-cli without authentication and
silences stderr, so authenticated Redis instances fail silently; update
loadRedisTestData to read the test Redis password from configuration
(testing.databases.redis.password), escape it (use escapeshellarg) and, when
non-empty, include the auth flag (e.g. -a <escapedPassword>) in both exec calls,
and stop redirecting stderr to /dev/null (or capture exec return codes) so
failures are visible; refer to loadRedisTestData, $server, $fixtureFile and the
config key testing.databases.redis.password when making the change.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #97 +/- ##
============================================
+ Coverage 89.01% 89.12% +0.10%
- Complexity 1314 1354 +40
============================================
Files 126 127 +1
Lines 4935 5049 +114
============================================
+ Hits 4393 4500 +107
- Misses 542 549 +7 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Summary
Changes
Core: DatabaseType::REDIS enum, RedisDatabase handler implementing DatabaseInterface, backup/restore task integration, connection tester via redis-cli PING/INFO, database list service, backup job factory
UI: Form hides database selection for Redis, username optional, SSH tunnel hidden; Index page shows manual restore instructions modal with link to backup snapshots
Infrastructure: Redis 7 Docker service, redis package in Dockerfile, GitHub Actions Redis service, test config
Tests: 500 tests passing - new tests for Redis backup commands, restore rejection, connection testing, integration backup workflow, Livewire components
Test plan
Summary by CodeRabbit
New Features
Changes
Documentation
Chores
Tests