├── .circleci └── config.yml ├── .docker ├── config │ ├── conf.d │ │ └── default.conf │ ├── config │ │ ├── conf.d │ │ │ └── default.conf │ │ ├── fpm-pool.conf │ │ ├── nginx.conf │ │ ├── php.ini │ │ └── supervisord.conf │ ├── fpm-pool.conf │ ├── nginx.conf │ ├── php.ini │ └── supervisord.conf ├── database-backup.sh ├── octane │ ├── .rr.prod.yaml │ ├── FrankenPHP │ │ └── supervisord.frankenphp.conf │ ├── RoadRunner │ │ ├── .rr.prod.yaml │ │ └── supervisord.roadrunner.conf │ ├── Swoole │ │ └── supervisord.swoole.conf │ ├── entrypoint.sh │ ├── opcache.ini │ ├── php.ini │ ├── supervisord.app.conf │ ├── supervisord.app.roadrunner.conf │ ├── supervisord.horizon.conf │ └── utilities.sh ├── php.ini ├── start-container ├── supervisord.conf ├── supervisord.horizon.conf ├── supervisord.scheduler.conf ├── supervisord.worker.conf └── utilities.sh ├── .env.example ├── .env.testing ├── .github ├── FUNDING.yml ├── dependabot.yml ├── issue_template.md └── workflows │ ├── install.yml │ ├── main.yml │ ├── security.yml │ └── tests.yml ├── .gitignore ├── Dockerfile ├── README.md ├── app ├── Actions │ ├── Fortify │ │ ├── CreateNewUser.php │ │ ├── CreateNewUserWithTeams.php │ │ ├── PasswordValidationRules.php │ │ ├── ResetUserPassword.php │ │ ├── UpdateUserPassword.php │ │ └── UpdateUserProfileInformation.php │ ├── Jetstream │ │ ├── AddTeamMember.php │ │ ├── CreateTeam.php │ │ ├── DeleteTeam.php │ │ ├── DeleteUser.php │ │ ├── DeleteUserWithTeams.php │ │ ├── InviteTeamMember.php │ │ ├── RemoveTeamMember.php │ │ └── UpdateTeamName.php │ └── Socialstream │ │ ├── CreateConnectedAccount.php │ │ ├── CreateUserFromProvider.php │ │ ├── CreateUserWithTeamsFromProvider.php │ │ ├── GenerateRedirectForProvider.php │ │ ├── HandleInvalidState.php │ │ ├── ResolveSocialiteUser.php │ │ ├── SetUserPassword.php │ │ └── UpdateConnectedAccount.php ├── Console │ ├── Kernel.php │ └── UpdateEmailServersCommand.php ├── Exceptions │ └── Handler.php ├── Facades │ └── SiteSettings.php ├── Filament │ ├── Admin │ │ └── Resources │ │ │ ├── BackupResource.php │ │ │ ├── MenuResource │ │ │ └── Pages │ │ │ │ ├── CreateMenu.php │ │ │ │ ├── EditMenu.php │ │ │ │ └── ListMenus.php │ │ │ ├── MonitoringResource.php │ │ │ └── SiteSettingsResource │ │ │ └── Pages │ │ │ ├── CreateSiteSettings.php │ │ │ ├── EditSiteSettings.php │ │ │ └── ListSiteSettings.php │ └── App │ │ ├── Pages │ │ ├── .gitignore │ │ ├── ApiTokens.php │ │ ├── CreateTeam.php │ │ ├── EditProfile.php │ │ ├── EditTeam.php │ │ ├── PersonalAccessTokensPage.php │ │ └── UpdateProfileInformationPage.php │ │ ├── Resources │ │ ├── .gitignore │ │ ├── DatabaseResource.php │ │ ├── DatabaseResource │ │ │ └── Pages │ │ │ │ ├── CreateResource.php │ │ │ │ ├── EditResource.php │ │ │ │ └── ListResources.php │ │ ├── DnsSettingResource.php │ │ ├── DnsSettingResource │ │ │ └── Pages │ │ │ │ ├── CreateDnsSetting.php │ │ │ │ ├── EditDnsSetting.php │ │ │ │ └── ListDnsSettings.php │ │ ├── DomainResource.php │ │ ├── DomainResource │ │ │ ├── ContainerRestarter.php │ │ │ ├── DovecotConfigGenerator.php │ │ │ ├── Pages │ │ │ │ ├── CreateDomain.php │ │ │ │ ├── EditDomain.php │ │ │ │ └── ListDomains.php │ │ │ └── PostfixConfigGenerator.php │ │ ├── EmailResource.php │ │ ├── EmailResource │ │ │ └── Pages │ │ │ │ └── ManageEmails.php │ │ ├── FileResource.php │ │ ├── FileResource │ │ │ └── Pages │ │ │ │ ├── EditFile.php │ │ │ │ └── ListFiles.php │ │ ├── HostingPlanResource.php │ │ ├── HostingPlanResource │ │ │ └── Pages │ │ │ │ ├── CreateHostingPlan.php │ │ │ │ ├── EditHostingPlan.php │ │ │ │ └── ListHostingPlans.php │ │ ├── UserHostingPlanResource.php │ │ ├── UserHostingPlanResource │ │ │ └── Pages │ │ │ │ ├── CreateUserHostingPlan.php │ │ │ │ ├── EditUserHostingPlan.php │ │ │ │ └── ListUserHostingPlans.php │ │ ├── UserResource.php │ │ └── UserResource │ │ │ └── Pages │ │ │ ├── CreateUser.php │ │ │ ├── EditUser.php │ │ │ └── ListUsers.php │ │ └── Widgets │ │ └── .gitignore ├── Http │ ├── Controllers │ │ ├── Controller.php │ │ ├── ForgotPasswordController.php │ │ ├── LoginController.php │ │ ├── ResetPasswordController.php │ │ └── TeamInvitationController.php │ ├── Kernel.php │ ├── Livewire │ │ └── CreateTeam.php │ └── Middleware │ │ ├── AssignDefaultTeam.php │ │ ├── Authenticate.php │ │ ├── EncryptCookies.php │ │ ├── PreventRequestsDuringMaintenance.php │ │ ├── RedirectIfAuthenticated.php │ │ ├── RoleBasedRedirect.php │ │ ├── ScreeningDataEncryptor.php │ │ ├── TeamsPermission.php │ │ ├── TrimStrings.php │ │ ├── TrustHosts.php │ │ ├── TrustProxies.php │ │ ├── ValidateSignature.php │ │ └── VerifyCsrfToken.php ├── Listeners │ ├── CreatePersonalTeam.php │ ├── DnsSettingSavedListener.php │ ├── EmailTracker.php │ ├── SwitchTeam.php │ └── UpdateBindDnsRecords.php ├── Models │ ├── AccessLog.php │ ├── ConnectedAccount.php │ ├── Database.php │ ├── DnsSetting.php │ ├── Domain.php │ ├── EmailAccount.php │ ├── HostingPlan.php │ ├── Membership.php │ ├── Menu.php │ ├── ResourceUsage.php │ ├── SiteSettings.php │ ├── Team.php │ ├── TeamInvitation.php │ ├── User.php │ └── UserHostingPlan.php ├── Policies │ ├── ConnectedAccountPolicy.php │ └── TeamPolicy.php ├── Providers │ ├── AppServiceProvider.php │ ├── AuthServiceProvider.php │ ├── BroadcastServiceProvider.php │ ├── DnsSettingSaved.php │ ├── DnsSettingService.php │ ├── DockerComposeService.php │ ├── DockerComposeServiceProvider.php │ ├── DockerServiceProvider.php │ ├── EventServiceProvider.php │ ├── Filament │ │ ├── AdminPanelProvider.php │ │ └── AppPanelProvider.php │ ├── FortifyServiceProvider.php │ ├── JetstreamServiceProvider.php │ ├── RouteServiceProvider.php │ ├── SiteSettingsServiceProvider.php │ ├── SocialstreamServiceProvider.php │ └── TeamServiceProvider.php ├── Services │ ├── BackupService.php │ ├── DockerComposeService.php │ ├── MonitoringService.php │ ├── MySqlDatabaseService.php │ ├── RestoreService.php │ └── SftpService.php └── View │ └── Components │ ├── AppLayout.php │ └── GuestLayout.php ├── artisan ├── bootstrap ├── app.php └── cache │ └── .gitignore ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── broadcasting.php ├── cache.php ├── cors.php ├── database.php ├── filesystems.php ├── fortify.php ├── hashing.php ├── jetstream.php ├── logging.php ├── mail.php ├── octane.php ├── queue.php ├── sanctum.php ├── services.php ├── session.php ├── socialstream.php └── view.php ├── database ├── .gitignore ├── factories │ ├── ConnectedAccountFactory.php │ ├── DatabaseFactory.php │ ├── DnsSettingFactory.php │ ├── EmailAccountFactory.php │ ├── HostingPlanFactory.php │ ├── MenuFactory.php │ ├── ResourceUsageFactory.php │ ├── TeamFactory.php │ ├── UserFactory.php │ └── UserHostingPlanFactory.php ├── migrations │ ├── 0001_01_01_000000_create_users_table.php │ ├── 0001_01_01_000001_make_password_nullable_on_users_table.php │ ├── 0001_01_01_000002_create_connected_accounts_table.php │ ├── 2014_10_12_200000_add_two_factor_columns_to_users_table.php │ ├── 2019_08_19_000000_create_failed_jobs_table.php │ ├── 2020_05_21_100000_create_teams_table.php │ ├── 2020_05_21_200000_create_team_user_table.php │ ├── 2020_05_21_300000_create_team_invitations_table.php │ ├── 2023_05_15_000000_create_site_settings_table.php │ ├── 2023_06_11_000000_create_databases_table.php │ ├── 2024_02_21_190705_create_permission_tables.php │ ├── 2024_06_13_125458_create_domains_table.php │ ├── 2024_06_14_125614_create_dns_settings_table.php │ ├── 2024_06_15_125159_create_hosting_plans_table.php │ ├── 2024_06_15_125338_create_user_hosting_plans_table.php │ ├── 2024_06_15_125614_add_priority_to_dns_settings_table.php │ ├── 2024_06_15_125713_create_email_accounts_table.php │ ├── 2024_06_15_125818_create_resource_usage_table.php │ ├── 2024_06_16_000000_add_hosting_plan_id_to_domains_table.php │ ├── 2024_07_24_080000_create_menus_table.php │ └── 2024_07_25_000000_add_sftp_ssh_credentials_to_domains_table.php └── seeders │ ├── DatabaseSeeder.php │ ├── HostingPlanSeeder.php │ ├── MenuSeeder.php │ ├── PermissionsSeeder.php │ ├── RolesSeeder.php │ └── SiteSettingsSeeder.php ├── docker-compose.secrets.yml ├── docker-compose.yml ├── package-lock.json ├── package.json ├── phpunit.xml ├── postcss.config.cjs ├── postcss.config.js ├── public ├── .htaccess ├── build │ ├── assets │ │ ├── app-DtDIA-zY.css │ │ ├── app-l0sNRNKZ.js │ │ └── theme-BA7Me4im.css │ ├── images │ │ └── logo.png │ └── manifest.json ├── css │ └── filament │ │ ├── filament │ │ └── app.css │ │ ├── forms │ │ └── forms.css │ │ └── support │ │ └── support.css ├── favicon.ico ├── index.php ├── js │ └── filament │ │ ├── filament │ │ ├── app.js │ │ └── echo.js │ │ ├── forms │ │ └── components │ │ │ ├── color-picker.js │ │ │ ├── date-time-picker.js │ │ │ ├── file-upload.js │ │ │ ├── key-value.js │ │ │ ├── markdown-editor.js │ │ │ ├── rich-editor.js │ │ │ ├── select.js │ │ │ ├── tags-input.js │ │ │ └── textarea.js │ │ ├── notifications │ │ └── notifications.js │ │ ├── support │ │ ├── async-alpine.js │ │ └── support.js │ │ ├── tables │ │ └── components │ │ │ └── table.js │ │ └── widgets │ │ └── components │ │ ├── chart.js │ │ └── stats-overview │ │ └── stat │ │ └── chart.js └── robots.txt ├── resources ├── css │ ├── app.css │ └── filament │ │ └── admin │ │ ├── tailwind.config.js │ │ └── theme.css ├── images │ └── logo.png ├── js │ └── app.js ├── markdown │ ├── policy.md │ └── terms.md └── views │ ├── api │ ├── api-token-manager.blade.php │ └── index.blade.php │ ├── auth │ ├── confirm-password.blade.php │ ├── forgot-password.blade.php │ ├── login.blade.php │ ├── register.blade.php │ ├── reset-password.blade.php │ ├── two-factor-challenge.blade.php │ └── verify-email.blade.php │ ├── components │ ├── action-link.blade.php │ ├── action-message.blade.php │ ├── action-section.blade.php │ ├── application-logo.blade.php │ ├── application-mark.blade.php │ ├── authentication-card-logo.blade.php │ ├── authentication-card.blade.php │ ├── banner.blade.php │ ├── button.blade.php │ ├── buttons.blade.php │ ├── checkbox.blade.php │ ├── confirmation-modal.blade.php │ ├── confirms-password.blade.php │ ├── connected-account.blade.php │ ├── contact-form.blade.php │ ├── danger-button.blade.php │ ├── dialog-modal.blade.php │ ├── dropdown-link.blade.php │ ├── dropdown.blade.php │ ├── footer.blade.php │ ├── form-section.blade.php │ ├── header.blade.php │ ├── home-header.blade.php │ ├── home-navbar.blade.php │ ├── input-error.blade.php │ ├── input.blade.php │ ├── label.blade.php │ ├── layouts │ │ └── app.blade.php │ ├── manage_section.blade.php │ ├── modal.blade.php │ ├── nav-link.blade.php │ ├── products_section.blade.php │ ├── responsive-nav-link.blade.php │ ├── secondary-button.blade.php │ ├── section-border.blade.php │ ├── section-title.blade.php │ ├── socialstream-icons │ │ ├── bitbucket.blade.php │ │ ├── facebook.blade.php │ │ ├── github.blade.php │ │ ├── gitlab.blade.php │ │ ├── google.blade.php │ │ ├── linkedin.blade.php │ │ ├── provider-icon.blade.php │ │ ├── slack.blade.php │ │ └── twitter.blade.php │ ├── socialstream.blade.php │ ├── switchable-team.blade.php │ ├── validation-errors.blade.php │ ├── welcome.blade.php │ └── why_us_section.blade.php │ ├── emails │ └── team-invitation.blade.php │ ├── filament │ ├── filament │ │ └── pages │ │ │ ├── api-tokens.blade.php │ │ │ ├── create-team.blade.php │ │ │ ├── edit-profile.blade.php │ │ │ └── edit-team.blade.php │ └── pages │ │ ├── api-tokens.blade.php │ │ ├── create-team.blade.php │ │ ├── edit-profile.blade.php │ │ └── edit-team.blade.php │ ├── layouts │ ├── app.blade.php │ └── guest.blade.php │ ├── policy.blade.php │ ├── profile │ ├── connected-accounts-form.blade.php │ ├── delete-user-form.blade.php │ ├── logout-other-browser-sessions-form.blade.php │ ├── set-password-form.blade.php │ ├── show.blade.php │ ├── two-factor-authentication-form.blade.php │ ├── update-password-form.blade.php │ └── update-profile-information-form.blade.php │ ├── teams │ ├── create-team-form.blade.php │ ├── create.blade.php │ ├── delete-team-form.blade.php │ ├── show.blade.php │ ├── team-member-manager.blade.php │ └── update-team-name-form.blade.php │ ├── terms.blade.php │ └── welcome.blade.php ├── routes ├── api.php ├── channels.php ├── console.php ├── socialstream.php └── web.php ├── rr ├── setup.sh ├── storage ├── app │ ├── .gitignore │ └── public │ │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── tailwind.config.js ├── tests ├── CreatesApplication.php ├── Feature │ ├── ApiTokenPermissionsTest.php │ ├── AuthenticationTest.php │ ├── BackupRestoreTest.php │ ├── BrowserSessionsTest.php │ ├── CreateApiTokenTest.php │ ├── CreateTeamTest.php │ ├── DeleteAccountTest.php │ ├── DeleteApiTokenTest.php │ ├── DeleteTeamTest.php │ ├── EmailVerificationTest.php │ ├── ExampleTest.php │ ├── FileManagementTest.php │ ├── InviteTeamMemberTest.php │ ├── LeaveTeamTest.php │ ├── MonitoringTest.php │ ├── PasswordConfirmationTest.php │ ├── PasswordResetTest.php │ ├── ProfileInformationTest.php │ ├── RegistrationTest.php │ ├── RemoveTeamMemberTest.php │ ├── SocialstreamRegistrationTest.php │ ├── TwoFactorAuthenticationSettingsTest.php │ ├── UpdatePasswordTest.php │ ├── UpdateTeamMemberRoleTest.php │ └── UpdateTeamNameTest.php ├── TestCase.php └── Unit │ └── ExampleTest.php └── vite.config.js /.docker/config/config/php.ini: -------------------------------------------------------------------------------- 1 | [Date] 2 | date.timezone="UTC" 3 | expose_php= Off -------------------------------------------------------------------------------- /.docker/config/config/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | logfile=/dev/null 4 | logfile_maxbytes=0 5 | pidfile=/run/supervisord.pid 6 | 7 | [program:php-fpm] 8 | command=php-fpm83 -F 9 | stdout_logfile=/dev/stdout 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/dev/stderr 12 | stderr_logfile_maxbytes=0 13 | autorestart=false 14 | startretries=0 15 | 16 | [program:nginx] 17 | command=nginx -g 'daemon off;' 18 | stdout_logfile=/dev/stdout 19 | stdout_logfile_maxbytes=0 20 | stderr_logfile=/dev/stderr 21 | stderr_logfile_maxbytes=0 22 | autorestart=false 23 | startretries=0 24 | -------------------------------------------------------------------------------- /.docker/config/php.ini: -------------------------------------------------------------------------------- 1 | [Date] 2 | date.timezone="UTC" 3 | expose_php= Off -------------------------------------------------------------------------------- /.docker/config/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | logfile=/dev/null 4 | logfile_maxbytes=0 5 | pidfile=/run/supervisord.pid 6 | 7 | [program:php-fpm] 8 | command=php-fpm83 -F 9 | stdout_logfile=/dev/stdout 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/dev/stderr 12 | stderr_logfile_maxbytes=0 13 | autorestart=false 14 | startretries=0 15 | 16 | [program:nginx] 17 | command=nginx -g 'daemon off;' 18 | stdout_logfile=/dev/stdout 19 | stdout_logfile_maxbytes=0 20 | stderr_logfile=/dev/stderr 21 | stderr_logfile_maxbytes=0 22 | autorestart=false 23 | startretries=0 24 | -------------------------------------------------------------------------------- /.docker/octane/.rr.prod.yaml: -------------------------------------------------------------------------------- 1 | version: '2.7' 2 | rpc: 3 | listen: 'tcp://127.0.0.1:6001' 4 | http: 5 | middleware: [ "static", "gzip", "headers" ] 6 | max_request_size: 20 7 | static: 8 | dir: "public" 9 | forbid: [ ".php", ".htaccess" ] 10 | uploads: 11 | forbid: [".php", ".exe", ".bat", ".sh"] 12 | pool: 13 | allocate_timeout: 10s 14 | destroy_timeout: 10s 15 | supervisor: 16 | max_worker_memory: 128 17 | exec_ttl: 60s 18 | logs: 19 | mode: production 20 | level: debug 21 | encoding: json 22 | -------------------------------------------------------------------------------- /.docker/octane/FrankenPHP/supervisord.frankenphp.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=%(ENV_USER)s 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [program:octane] 8 | process_name=%(program_name)s_%(process_num)02d 9 | command=php %(ENV_ROOT)s/artisan octane:start --server=frankenphp --host=0.0.0.0 --port=80 --admin-port=2019 10 | user=%(ENV_USER)s 11 | autostart=true 12 | autorestart=true 13 | environment=LARAVEL_OCTANE="1" 14 | stdout_logfile=/dev/stdout 15 | stdout_logfile_maxbytes=0 16 | stderr_logfile=/dev/stderr 17 | stderr_logfile_maxbytes=0 18 | 19 | [program:horizon] 20 | process_name=%(program_name)s_%(process_num)02d 21 | command=php %(ENV_ROOT)s/artisan horizon 22 | user=%(ENV_USER)s 23 | autostart=%(ENV_WITH_HORIZON)s 24 | autorestart=true 25 | stdout_logfile=%(ENV_ROOT)s/horizon.log 26 | stopwaitsecs=3600 27 | 28 | [program:scheduler] 29 | process_name=%(program_name)s_%(process_num)02d 30 | command=supercronic -overlapping /etc/supercronic/laravel 31 | user=%(ENV_USER)s 32 | autostart=%(ENV_WITH_SCHEDULER)s 33 | autorestart=true 34 | stdout_logfile=%(ENV_ROOT)s/scheduler.log 35 | 36 | [program:clear-scheduler-cache] 37 | process_name=%(program_name)s_%(process_num)02d 38 | command=php %(ENV_ROOT)s/artisan schedule:clear-cache 39 | user=%(ENV_USER)s 40 | autostart=%(ENV_WITH_SCHEDULER)s 41 | autorestart=false 42 | stdout_logfile=%(ENV_ROOT)s/scheduler.log -------------------------------------------------------------------------------- /.docker/octane/RoadRunner/.rr.prod.yaml: -------------------------------------------------------------------------------- 1 | version: '2.7' 2 | rpc: 3 | listen: 'tcp://127.0.0.1:6001' 4 | server: 5 | relay: pipes 6 | http: 7 | middleware: [ "static", "gzip", "headers" ] 8 | max_request_size: 20 9 | static: 10 | dir: "public" 11 | forbid: [ ".php", ".htaccess" ] 12 | uploads: 13 | forbid: [".php", ".exe", ".bat", ".sh"] 14 | pool: 15 | allocate_timeout: 10s 16 | destroy_timeout: 10s 17 | supervisor: 18 | max_worker_memory: 128 19 | exec_ttl: 60s 20 | logs: 21 | mode: production 22 | level: debug 23 | encoding: json 24 | status: 25 | address: localhost:2114 26 | -------------------------------------------------------------------------------- /.docker/octane/RoadRunner/supervisord.roadrunner.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=%(ENV_USER)s 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [program:octane] 8 | process_name=%(program_name)s_%(process_num)02d 9 | command=php %(ENV_ROOT)s/artisan octane:start --server=roadrunner --host=0.0.0.0 --port=80 --rpc-port=6001 --rr-config=%(ENV_ROOT)s/.rr.yaml 10 | user=%(ENV_USER)s 11 | autostart=true 12 | autorestart=true 13 | environment=LARAVEL_OCTANE="1" 14 | stdout_logfile=/dev/stdout 15 | stdout_logfile_maxbytes=0 16 | stderr_logfile=/dev/stderr 17 | stderr_logfile_maxbytes=0 18 | 19 | [program:horizon] 20 | process_name=%(program_name)s_%(process_num)02d 21 | command=php %(ENV_ROOT)s/artisan horizon 22 | user=%(ENV_USER)s 23 | autostart=%(ENV_WITH_HORIZON)s 24 | autorestart=true 25 | stdout_logfile=%(ENV_ROOT)s/horizon.log 26 | stopwaitsecs=3600 27 | 28 | [program:scheduler] 29 | process_name=%(program_name)s_%(process_num)02d 30 | command=supercronic -overlapping /etc/supercronic/laravel 31 | user=%(ENV_USER)s 32 | autostart=%(ENV_WITH_SCHEDULER)s 33 | autorestart=true 34 | stdout_logfile=%(ENV_ROOT)s/scheduler.log 35 | 36 | [program:clear-scheduler-cache] 37 | process_name=%(program_name)s_%(process_num)02d 38 | command=php %(ENV_ROOT)s/artisan schedule:clear-cache 39 | user=%(ENV_USER)s 40 | autostart=%(ENV_WITH_SCHEDULER)s 41 | autorestart=false 42 | stdout_logfile=%(ENV_ROOT)s/scheduler.log -------------------------------------------------------------------------------- /.docker/octane/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | container_mode=${CONTAINER_MODE:-app} 5 | octane_server=${OCTANE_SERVER:-swoole} 6 | echo "Container mode: $container_mode" 7 | 8 | php() { 9 | su octane -c "php $*" 10 | } 11 | 12 | initialStuff() { 13 | php artisan optimize:clear; \ 14 | php artisan package:discover --ansi; \ 15 | php artisan event:cache; \ 16 | php artisan config:cache; \ 17 | php artisan route:cache; 18 | } 19 | 20 | if [ "$1" != "" ]; then 21 | exec "$@" 22 | elif [ ${container_mode} = "app" ]; then 23 | echo "Octane server: $octane_server" 24 | initialStuff 25 | if [ ${octane_server} = "swoole" ]; then 26 | exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.app.conf 27 | elif [ ${octane_server} = "roadrunner" ]; then 28 | exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.app.roadrunner.conf 29 | else 30 | echo "Invalid Octane server supplied." 31 | exit 1 32 | fi 33 | elif [ ${container_mode} = "horizon" ]; then 34 | initialStuff 35 | exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.horizon.conf 36 | elif [ ${container_mode} = "scheduler" ]; then 37 | initialStuff 38 | exec supercronic /etc/supercronic/laravel 39 | else 40 | echo "Container mode mismatched." 41 | exit 1 42 | fi 43 | -------------------------------------------------------------------------------- /.docker/octane/opcache.ini: -------------------------------------------------------------------------------- 1 | [Opcache] 2 | opcache.enable = 1 3 | opcache.enable_cli = 1 4 | opcache.memory_consumption = 256M 5 | opcache.use_cwd = 0 6 | opcache.max_file_size = 0 7 | opcache.max_accelerated_files = 32531 8 | opcache.validate_timestamps = 0 9 | opcache.revalidate_freq = 0 10 | 11 | [JIT] 12 | opcache.jit_buffer_size = 100M 13 | opcache.jit = function -------------------------------------------------------------------------------- /.docker/octane/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | post_max_size = 100M 3 | upload_max_filesize = 100M 4 | expose_php = 0 5 | variables_order = "GPCS" 6 | -------------------------------------------------------------------------------- /.docker/octane/supervisord.app.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [program:octane] 8 | process_name=%(program_name)s_%(process_num)02d 9 | command=php /var/www/html/artisan octane:start --server=swoole --host=0.0.0.0 --port=9000 --workers=auto --task-workers=auto --max-requests=500 10 | user=octane 11 | autostart=true 12 | autorestart=true 13 | environment=LARAVEL_OCTANE="1" 14 | stdout_logfile=/dev/stdout 15 | stdout_logfile_maxbytes=0 16 | stderr_logfile=/dev/stderr 17 | stderr_logfile_maxbytes=0 18 | 19 | [program:horizon] 20 | process_name=%(program_name)s_%(process_num)02d 21 | command=php /var/www/html/artisan horizon 22 | user=octane 23 | autostart=%(ENV_APP_WITH_HORIZON)s 24 | autorestart=true 25 | stdout_logfile=/var/www/html/horizon.log 26 | stopwaitsecs=3600 27 | 28 | [program:scheduler] 29 | process_name=%(program_name)s_%(process_num)02d 30 | command=supercronic /etc/supercronic/laravel 31 | user=octane 32 | autostart=%(ENV_APP_WITH_SCHEDULER)s 33 | autorestart=true 34 | stdout_logfile=/var/www/html/scheduler.log -------------------------------------------------------------------------------- /.docker/octane/supervisord.app.roadrunner.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [program:octane] 8 | process_name=%(program_name)s_%(process_num)02d 9 | command=php /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --port=9000 --rpc-port=6001 --workers=auto --max-requests=500 --rr-config=/var/www/html/.rr.yaml 10 | user=octane 11 | autostart=true 12 | autorestart=true 13 | environment=LARAVEL_OCTANE="1" 14 | stdout_logfile=/dev/stdout 15 | stdout_logfile_maxbytes=0 16 | stderr_logfile=/dev/stderr 17 | stderr_logfile_maxbytes=0 18 | 19 | [program:horizon] 20 | process_name=%(program_name)s_%(process_num)02d 21 | command=php /var/www/html/artisan horizon 22 | user=octane 23 | autostart=%(ENV_APP_WITH_HORIZON)s 24 | autorestart=true 25 | stdout_logfile=/var/www/html/horizon.log 26 | stopwaitsecs=3600 27 | 28 | [program:scheduler] 29 | process_name=%(program_name)s_%(process_num)02d 30 | command=supercronic /etc/supercronic/laravel 31 | user=octane 32 | autostart=%(ENV_APP_WITH_SCHEDULER)s 33 | autorestart=true 34 | stdout_logfile=/var/www/html/scheduler.log 35 | -------------------------------------------------------------------------------- /.docker/octane/supervisord.horizon.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [program:horizon] 8 | process_name=%(program_name)s_%(process_num)02d 9 | command=php /var/www/html/artisan horizon 10 | user=octane 11 | autostart=true 12 | autorestart=true 13 | stdout_logfile=/dev/stdout 14 | stdout_logfile_maxbytes=0 15 | stderr_logfile=/dev/stderr 16 | stderr_logfile_maxbytes=0 17 | stopwaitsecs=3600 18 | -------------------------------------------------------------------------------- /.docker/octane/utilities.sh: -------------------------------------------------------------------------------- 1 | php() { 2 | echo "Running PHP as octane user ..." 3 | su octane -c "php $*" 4 | } 5 | 6 | tinker() { 7 | if [ -z "$1" ]; then 8 | php artisan tinker 9 | else 10 | php artisan tinker --execute="\"dd($1);\"" 11 | fi 12 | } 13 | 14 | # Determine size of a file or total size of a directory 15 | fs() { 16 | if du -b /dev/null >/dev/null 2>&1; then 17 | local arg=-sbh 18 | else 19 | local arg=-sh 20 | fi 21 | if [[ -n "$@" ]]; then 22 | du $arg -- "$@" 23 | else 24 | du $arg .[^.]* ./* 25 | fi 26 | } 27 | 28 | # Commonly used aliases 29 | alias ..="cd .." 30 | alias ...="cd ../.." 31 | alias art="php artisan" 32 | -------------------------------------------------------------------------------- /.docker/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | post_max_size = 100M 3 | upload_max_filesize = 100M 4 | expose_php = 0 5 | realpath_cache_size = 16M 6 | realpath_cache_ttl = 360 7 | 8 | [Opcache] 9 | opcache.enable = 1 10 | opcache.enable_cli = 1 11 | opcache.memory_consumption = 256M 12 | opcache.use_cwd = 0 13 | opcache.max_file_size = 0 14 | opcache.max_accelerated_files = 32531 15 | opcache.validate_timestamps = 0 16 | opcache.file_update_protection = 0 17 | opcache.interned_strings_buffer = 16 18 | opcache.file_cache = 60 19 | 20 | [JIT] 21 | opcache.jit_buffer_size = 128M 22 | opcache.jit = function 23 | opcache.jit_prof_threshold = 0.001 24 | opcache.jit_max_root_traces = 2048 25 | opcache.jit_max_side_traces = 256 26 | 27 | [zlib] 28 | zlib.output_compression = On 29 | zlib.output_compression_level = 9 30 | -------------------------------------------------------------------------------- /.docker/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=%(ENV_USER)s 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [unix_http_server] 8 | file=/var/run/supervisor.sock 9 | 10 | [supervisorctl] 11 | serverurl=unix:///var/run/supervisor.sock 12 | 13 | [rpcinterface:supervisor] 14 | supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface 15 | -------------------------------------------------------------------------------- /.docker/supervisord.horizon.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=%(ENV_USER)s 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [program:horizon] 8 | process_name=%(program_name)s_%(process_num)02d 9 | command=php %(ENV_ROOT)s/artisan horizon 10 | user=%(ENV_USER)s 11 | autostart=true 12 | autorestart=true 13 | stdout_logfile=/dev/stdout 14 | stdout_logfile_maxbytes=0 15 | stderr_logfile=/dev/stderr 16 | stderr_logfile_maxbytes=0 17 | stopwaitsecs=3600 18 | -------------------------------------------------------------------------------- /.docker/supervisord.scheduler.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=%(ENV_USER)s 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [program:scheduler] 8 | process_name=%(program_name)s_%(process_num)02d 9 | command=supercronic -overlapping /etc/supercronic/laravel 10 | user=%(ENV_USER)s 11 | autostart=%(ENV_WITH_SCHEDULER)s 12 | autorestart=true 13 | stdout_logfile=/dev/stdout 14 | stdout_logfile_maxbytes=0 15 | stderr_logfile=/dev/stderr 16 | stderr_logfile_maxbytes=0 17 | 18 | [program:clear-scheduler-cache] 19 | process_name=%(program_name)s_%(process_num)02d 20 | command=php %(ENV_ROOT)s/artisan schedule:clear-cache 21 | user=%(ENV_USER)s 22 | autostart=%(ENV_WITH_SCHEDULER)s 23 | autorestart=false 24 | stdout_logfile=/dev/stdout 25 | stdout_logfile_maxbytes=0 26 | stderr_logfile=/dev/stderr 27 | stderr_logfile_maxbytes=0 -------------------------------------------------------------------------------- /.docker/supervisord.worker.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=%(ENV_USER)s 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [program:worker] 8 | process_name=%(program_name)s_%(process_num)02d 9 | command=%(ENV_WORKER_COMMAND)s 10 | user=%(ENV_USER)s 11 | autostart=true 12 | autorestart=true 13 | stdout_logfile=/dev/stdout 14 | stdout_logfile_maxbytes=0 15 | stderr_logfile=/dev/stderr 16 | stderr_logfile_maxbytes=0 17 | -------------------------------------------------------------------------------- /.docker/utilities.sh: -------------------------------------------------------------------------------- 1 | tinker() { 2 | if [ -z "$1" ]; then 3 | php artisan tinker 4 | else 5 | php artisan tinker --execute="\"dd($1);\"" 6 | fi 7 | } 8 | 9 | # Commonly used aliases 10 | alias ..="cd .." 11 | alias ...="cd ../.." 12 | alias art="php artisan" 13 | -------------------------------------------------------------------------------- /.env.testing: -------------------------------------------------------------------------------- 1 | APP_NAME=Liberu 2 | APP_ENV=testing 3 | APP_KEY=base64:GS0VwCqzgSVBnM0Wz/Ig610q86M+GIvyVmzQQvrL7Xw= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | 7 | OWNER_COMPANY_ID=1 8 | DB_TENANT_DATABASE=tenant 9 | 10 | LOG_CHANNEL=stack 11 | 12 | DB_CONNECTION=mysql 13 | DB_HOST=localhost 14 | DB_PORT=3306 15 | DB_DATABASE=liberu 16 | DB_USERNAME=root 17 | DB_PASSWORD=root 18 | 19 | BROADCAST_DRIVER=log 20 | CACHE_DRIVER=file 21 | FILESYSTEM_DISK=local 22 | SESSION_DRIVER=file 23 | SESSION_LIFETIME=120 24 | 25 | CACHE_LIFETIME=60 26 | 27 | LOGIN_ATTEMPTS_PER_MINUTE=5 28 | PASSWORD_LIFETIME=0 29 | PASSWORD_MIN_LENGTH=6 30 | PASSWORD_MIXED_CASE=0 31 | PASSWORD_NUMERIC=0 32 | PASSWORD_SPECIAL=0 33 | 34 | REDIS_HOST=127.0.0.1 35 | REDIS_PASSWORD=null 36 | REDIS_PORT=6379 37 | 38 | MAIL_MAILER=log 39 | MAIL_HOST= 40 | MAIL_PORT= 41 | MAIL_USERNAME=null 42 | MAIL_PASSWORD=null 43 | MAIL_ENCRYPTION=null 44 | MAIL_FROM_ADDRESS=null 45 | MAIL_FROM_NAME="${APP_NAME}" 46 | 47 | MAILGUN_DOMAIN= 48 | MAILGUN_SECRET= 49 | MAILGUN_ENDPOINT=api.mailgun.net 50 | 51 | AWS_ACCESS_KEY_ID= 52 | AWS_SECRET_ACCESS_KEY= 53 | AWS_DEFAULT_REGION=us-east-1 54 | AWS_BUCKET= 55 | 56 | PUSHER_APP_ID= 57 | PUSHER_APP_KEY= 58 | PUSHER_APP_SECRET= 59 | PUSHER_APP_CLUSTER=mt1 60 | 61 | SENTRY_LARAVEL_DSN= 62 | 63 | TELESCOPE_ENABLED=0 64 | SCOUT_DRIVER=null 65 | 66 | ENSO_API_TOKEN= 67 | 68 | TINY_MCE_API_KEY= 69 | 70 | SANCTUM_STATEFUL_DOMAINS=localhost,127.0.0.1,127.0.0.1:8000,127.0.0.1:3000,localhost:3000,localhost:8080,::1 71 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: laravel-liberu 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "composer" 4 | # Files stored in repository root 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | - package-ecosystem: "npm" 9 | # Files stored in repository root 10 | directory: "/" 11 | schedule: 12 | interval: "daily" 13 | - package-ecosystem: "github-actions" 14 | # Workflow files stored in the 15 | # default location of `.github/workflows` 16 | directory: "/" 17 | schedule: 18 | interval: "daily" 19 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 2 | This is a **bug | feature request**. 3 | 4 | 5 | ### Prerequisites 6 | * [ ] Are you running the latest version? 7 | * [ ] Are you reporting to the correct repository? 8 | * [ ] Did you check the documentation? 9 | * [ ] Did you perform a cursory search? 10 | 11 | ### Description 12 | 13 | 14 | ### Steps to Reproduce 15 | 20 | 21 | ### Expected behavior 22 | 23 | 24 | ### Actual behavior 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.github/workflows/install.yml: -------------------------------------------------------------------------------- 1 | name: Install 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | install: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Setup PHP 18 | uses: shivammathur/setup-php@v2 19 | with: 20 | php-version: '8.3' 21 | 22 | - name: Setup Node.js 23 | uses: actions/setup-node@v4 24 | with: 25 | node-version: '20' 26 | 27 | 28 | - name: Start database 29 | run: sudo /etc/init.d/mysql start 30 | 31 | - name: Create database 32 | run: mysql -e "CREATE DATABASE IF NOT EXISTS liberu;" -uroot -proot 33 | 34 | - name: Copy environment file 35 | run: cp .env.testing .env 36 | 37 | - name: Install dependencies 38 | run: composer install 39 | 40 | - name: Generate application key 41 | run: php artisan key:generate 42 | 43 | - name: Run database migrations 44 | run: php artisan migrate 45 | 46 | - name: Seed database 47 | run: php artisan db:seed 48 | 49 | - name: Install npm dependencies 50 | run: npm install 51 | 52 | - name: Build frontend assets 53 | run: npm run build 54 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | deployment: 9 | workflow_dispatch: 10 | 11 | env: 12 | DB_DATABASE: liberu 13 | DB_USERNAME: liberu 14 | DB_PASSWORD: secret 15 | 16 | jobs: 17 | docker: 18 | if: github.event_name == 'push' 19 | runs-on: ubuntu-latest 20 | steps: 21 | - 22 | name: Login to Docker Hub 23 | uses: docker/login-action@v3 24 | with: 25 | username: ${{ secrets.DOCKERHUB_USERNAME }} 26 | password: ${{ secrets.DOCKERHUB_TOKEN }} 27 | 28 | - 29 | name: Extract metadata (tags, labels) for Docker 30 | id: meta 31 | uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 32 | with: 33 | images: liberu/control-panel 34 | 35 | - 36 | # Setting up Docker Buildx with docker-container driver is required 37 | # at the moment to be able to use a subdirectory with Git context 38 | name: Set up Docker Buildx 39 | uses: docker/setup-buildx-action@v3 40 | 41 | - name: Build and push Docker image 42 | uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 43 | with: 44 | # context: "{{defaultContext}}:.docker/prod/app/" 45 | file: Dockerfile 46 | push: true 47 | tags: ${{ steps.meta.outputs.tags }} 48 | labels: ${{ steps.meta.outputs.labels }} 49 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | tests: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Setup PHP 18 | uses: shivammathur/setup-php@v2 19 | with: 20 | php-version: '8.3' 21 | 22 | 23 | - name: Start database 24 | run: sudo /etc/init.d/mysql start 25 | 26 | - name: Create database 27 | run: mysql -e "CREATE DATABASE IF NOT EXISTS liberu;" -uroot -proot 28 | 29 | - name: Copy environment file 30 | run: cp .env.testing .env 31 | 32 | - name: Install dependencies 33 | run: composer install 34 | 35 | - name: Generate application key 36 | run: php artisan key:generate 37 | 38 | - name: Run database migrations 39 | run: php artisan migrate 40 | 41 | - name: Seed database 42 | run: php artisan db:seed 43 | 44 | - name: Run tests 45 | run: vendor/bin/phpunit --coverage-clover=coverage.xml 46 | 47 | - name: Upload coverage to Codecov 48 | uses: codecov/codecov-action@v5 49 | with: 50 | token: ${{ secrets.CODECOV_TOKEN }} 51 | files: ./coverage.xml 52 | flags: unittests 53 | name: codecov-umbrella 54 | fail_ci_if_error: true 55 | 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | 3 | 4 | 5 | 6 | node_modules/ 7 | npm-debug.log 8 | yarn-error.log 9 | 10 | # Laravel 4 specific 11 | bootstrap/compiled.php 12 | app/storage/ 13 | 14 | # Laravel 5 & Lumen specific 15 | public/storage 16 | public/hot 17 | 18 | # Laravel 5 & Lumen specific with changed public path 19 | public_html/storage 20 | public_html/hot 21 | 22 | storage/*.key 23 | .env 24 | Homestead.yaml 25 | Homestead.json 26 | /.vagrant 27 | .phpunit.result.cache 28 | rr 29 | .rr.yaml 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.1-fpm 2 | 3 | # Install system dependencies 4 | RUN apt-get update && apt-get install -y \ 5 | git \ 6 | curl \ 7 | libpng-dev \ 8 | libonig-dev \ 9 | libxml2-dev \ 10 | zip \ 11 | unzip 12 | 13 | # Clear cache 14 | RUN apt-get clean && rm -rf /var/lib/apt/lists/* 15 | 16 | # Install PHP extensions 17 | RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd 18 | 19 | # Get latest Composer 20 | COPY --from=composer:latest /usr/bin/composer /usr/bin/composer 21 | 22 | # Set working directory 23 | WORKDIR /var/www/html 24 | 25 | # Copy existing application directory contents 26 | COPY . /var/www/html 27 | 28 | # Install application dependencies 29 | RUN composer install 30 | 31 | # Generate application key 32 | RUN php artisan key:generate 33 | 34 | # Set permissions 35 | RUN chown -R www-data:www-data /var/www/html \ 36 | && chmod -R 755 /var/www/html/storage 37 | 38 | # Expose port 9000 and start php-fpm server 39 | EXPOSE 9000 40 | CMD ["php-fpm"] 41 | -------------------------------------------------------------------------------- /app/Actions/Fortify/PasswordValidationRules.php: -------------------------------------------------------------------------------- 1 | |string> 13 | */ 14 | protected function passwordRules(): array 15 | { 16 | return ['required', 'string', Password::default(), 'confirmed']; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Actions/Jetstream/CreateTeam.php: -------------------------------------------------------------------------------- 1 | $input 19 | */ 20 | public function create(User $user, array $input): Team 21 | { 22 | Gate::forUser($user)->authorize('create', Jetstream::newTeamModel()); 23 | 24 | Validator::make($input, [ 25 | 'name' => ['required', 'string', 'max:255'], 26 | ])->validateWithBag('createTeam'); 27 | 28 | AddingTeam::dispatch($user); 29 | 30 | $user->switchTeam($team = $user->ownedTeams()->create([ 31 | 'name' => $input['name'], 32 | 'personal_team' => true, 33 | ])); 34 | 35 | return $team; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/Actions/Jetstream/DeleteTeam.php: -------------------------------------------------------------------------------- 1 | purge(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/Actions/Jetstream/DeleteUser.php: -------------------------------------------------------------------------------- 1 | deleteProfilePhoto(); 18 | $user->tokens->each->delete(); 19 | $user->connectedAccounts->each->delete(); 20 | $user->delete(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Actions/Jetstream/DeleteUserWithTeams.php: -------------------------------------------------------------------------------- 1 | deleteTeams($user); 27 | $user->deleteProfilePhoto(); 28 | $user->tokens->each->delete(); 29 | $user->delete(); 30 | }); 31 | } 32 | 33 | /** 34 | * Delete the teams and team associations attached to the user. 35 | */ 36 | protected function deleteTeams(User $user): void 37 | { 38 | $user->teams()->detach(); 39 | 40 | $user->ownedTeams->each(function (Team $team) { 41 | $this->deletesTeams->delete($team); 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/Actions/Jetstream/UpdateTeamName.php: -------------------------------------------------------------------------------- 1 | $input 17 | */ 18 | public function update(User $user, Team $team, array $input): void 19 | { 20 | Gate::forUser($user)->authorize('update', $team); 21 | 22 | Validator::make($input, [ 23 | 'name' => ['required', 'string', 'max:255'], 24 | ])->validateWithBag('updateTeamName'); 25 | 26 | $team->forceFill([ 27 | 'name' => $input['name'], 28 | ])->save(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Actions/Socialstream/CreateConnectedAccount.php: -------------------------------------------------------------------------------- 1 | $user->id, 19 | 'provider' => strtolower($provider), 20 | 'provider_id' => $providerUser->getId(), 21 | 'name' => $providerUser->getName(), 22 | 'nickname' => $providerUser->getNickname(), 23 | 'email' => $providerUser->getEmail(), 24 | 'avatar_path' => $providerUser->getAvatar(), 25 | 'token' => $providerUser->token, 26 | 'secret' => $providerUser->tokenSecret ?? null, 27 | 'refresh_token' => $providerUser->refreshToken ?? null, 28 | 'expires_at' => property_exists($providerUser, 'expiresIn') ? now()->addSeconds($providerUser->expiresIn) : null, 29 | ]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Actions/Socialstream/GenerateRedirectForProvider.php: -------------------------------------------------------------------------------- 1 | redirect(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Actions/Socialstream/HandleInvalidState.php: -------------------------------------------------------------------------------- 1 | user(); 18 | 19 | if (Socialstream::generatesMissingEmails()) { 20 | $user->email = $user->getEmail() ?? ("{$user->id}@{$provider}".config('app.domain')); 21 | } 22 | 23 | return $user; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Actions/Socialstream/SetUserPassword.php: -------------------------------------------------------------------------------- 1 | ['required', 'string', Password::default(), 'confirmed'], 19 | ])->validateWithBag('setPassword'); 20 | 21 | $user->forceFill([ 22 | 'password' => Hash::make($input['password']), 23 | ])->save(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Console/UpdateEmailServersCommand.php: -------------------------------------------------------------------------------- 1 | info('Updating Dovecot and Postfix Docker instances...'); 17 | 18 | $containerRestarter = new ContainerRestarter(); 19 | $containerRestarter->restart(); 20 | 21 | $this->info('Dovecot and Postfix Docker instances updated successfully.'); 22 | } 23 | } -------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $dontFlash = [ 16 | 'current_password', 17 | 'password', 18 | 'password_confirmation', 19 | ]; 20 | 21 | /** 22 | * Register the exception handling callbacks for the application. 23 | */ 24 | public function register(): void 25 | { 26 | $this->reportable(function (Throwable $e) { 27 | // 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Facades/SiteSettings.php: -------------------------------------------------------------------------------- 1 | user()->canCreateTeams(), 403); 23 | } 24 | 25 | protected function getFormSchema(): array 26 | { 27 | return [ 28 | TextInput::make('name') 29 | ->label('Team Name') 30 | ->required() 31 | ->maxLength(255), 32 | ]; 33 | } 34 | 35 | protected function handleRegistration(array $data): Model 36 | { 37 | return app(\App\Actions\Jetstream\CreateTeam::class)->create(auth()->user(), $data); 38 | } 39 | 40 | public function getBreadcrumbs(): array 41 | { 42 | return [ 43 | url()->current() => 'Create Team', 44 | ]; 45 | } 46 | 47 | public static function getLabel(): string 48 | { 49 | return 'Create Team'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/Filament/App/Pages/PersonalAccessTokensPage.php: -------------------------------------------------------------------------------- 1 | user = Auth::user(); 26 | } 27 | 28 | public function createApiToken(string $name): void 29 | { 30 | $this->user->createToken($name); 31 | } 32 | 33 | public function deleteApiToken(string $name): void 34 | { 35 | $this->user->tokens()->where('name', $name)->first()->delete(); 36 | } 37 | 38 | public function getHeading(): string 39 | { 40 | return static::$title; 41 | } 42 | 43 | public static function shouldRegisterNavigation(): bool 44 | { 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/Filament/App/Resources/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-control-panel/control-panel-laravel/9a25e557cbddb15c4bbe051663d650f0b336a78d/app/Filament/App/Resources/.gitignore -------------------------------------------------------------------------------- /app/Filament/App/Resources/DatabaseResource/Pages/CreateResource.php: -------------------------------------------------------------------------------- 1 | user(); 20 | $databaseService = app(MySqlDatabaseService::class); 21 | 22 | if ($databaseService->createDatabase($data['name'], $data['charset'], $data['collation'])) { 23 | $data['user_id'] = $user->id; 24 | return static::getModel()::create($data); 25 | } else { 26 | Notification::make() 27 | ->title('Database Creation Failed') 28 | ->body('Failed to create the database in MySQL. Please try again.') 29 | ->danger() 30 | ->send(); 31 | 32 | $this->halt(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/Filament/App/Resources/DatabaseResource/Pages/ListResources.php: -------------------------------------------------------------------------------- 1 | where('user_id', auth()->id()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Filament/App/Resources/DnsSettingResource/Pages/EditDnsSetting.php: -------------------------------------------------------------------------------- 1 | form->getState(); 28 | $this->record->update($data); 29 | 30 | $this->dnsSettingService->updateBindDnsRecord($this->record); 31 | 32 | Notification::make() 33 | ->title('DNS Setting Updated') 34 | ->body('The DNS setting has been updated and BIND records have been updated.') 35 | ->success() 36 | ->send(); 37 | 38 | if ($shouldRedirect) { 39 | $this->redirect($this->getRedirectUrl()); 40 | } 41 | if ($shouldSendSavedNotification) { 42 | $this->notify('saved'); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Filament/App/Resources/DnsSettingResource/Pages/ListDnsSettings.php: -------------------------------------------------------------------------------- 1 | setWorkingDirectory(base_path()); 14 | $process->run(); 15 | 16 | if (!$process->isSuccessful()) { 17 | throw new \RuntimeException('Failed to restart containers: ' . $process->getErrorOutput()); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /app/Filament/App/Resources/DomainResource/Pages/ListDomains.php: -------------------------------------------------------------------------------- 1 | update($data); 24 | 25 | return $record; 26 | } 27 | 28 | protected function getRedirectUrl(): string 29 | { 30 | return $this->getResource()::getUrl('index'); 31 | } 32 | } -------------------------------------------------------------------------------- /app/Filament/App/Resources/FileResource/Pages/ListFiles.php: -------------------------------------------------------------------------------- 1 | where('user_id', auth()->id()); 24 | } 25 | } -------------------------------------------------------------------------------- /app/Filament/App/Resources/HostingPlanResource/Pages/CreateHostingPlan.php: -------------------------------------------------------------------------------- 1 | validate(['email' => 'required|email']); 19 | 20 | $status = Password::broker('admins')->sendResetLink( 21 | $request->only('email') 22 | ); 23 | 24 | return $status === Password::RESET_LINK_SENT 25 | ? back()->with(['status' => __($status)]) 26 | : back()->withErrors(['email' => __($status)]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Http/Controllers/LoginController.php: -------------------------------------------------------------------------------- 1 | validate([ 18 | 'email' => ['required', 'email'], 19 | 'password' => ['required'], 20 | ]); 21 | 22 | if (Auth::guard('admin')->attempt($credentials)) { 23 | $request->session()->regenerate(); 24 | 25 | return redirect()->intended('/admin'); 26 | } 27 | 28 | return back()->withErrors([ 29 | 'email' => 'The provided credentials do not match our records.', 30 | ]); 31 | } 32 | 33 | public function logout(Request $request) 34 | { 35 | Auth::guard('admin')->logout(); 36 | $request->session()->invalidate(); 37 | $request->session()->regenerateToken(); 38 | 39 | return redirect('/admin/login'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Http/Livewire/CreateTeam.php: -------------------------------------------------------------------------------- 1 | validate(); 19 | 20 | $team = app(CreateTeam::class)->create( 21 | Auth::user(), 22 | ['name' => $this->state['name']] 23 | ); 24 | 25 | return redirect()->route('filament.pages.edit-team', ['team' => $team]); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Http/Middleware/AssignDefaultTeam.php: -------------------------------------------------------------------------------- 1 | check()) { 15 | $user = auth()->user(); 16 | $defaultTeam = $user->currentTeam ?? $user->ownedTeams()->first(); 17 | if (!$defaultTeam) { 18 | $defaultTeam = $user->ownedTeams()->create([ 19 | 'name' => $user->name . "'s Team", 20 | 'personal_team' => true, 21 | ]); 22 | $user->current_team_id = $defaultTeam->id; 23 | $user->save(); 24 | } 25 | Filament::setTenant($defaultTeam); 26 | } 27 | return $next($request); 28 | } 29 | } -------------------------------------------------------------------------------- /app/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | expectsJson() ? null : route('login'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /app/Http/Middleware/PreventRequestsDuringMaintenance.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /app/Http/Middleware/ScreeningDataEncryptor.php: -------------------------------------------------------------------------------- 1 | getContent()) { 15 | $content = json_decode($response->getContent(), true); 16 | 17 | $fieldsToEncrypt = [ 18 | 'background_check_status', 19 | 'credit_report_status', 20 | 'rental_history_status' 21 | ]; 22 | 23 | foreach ($fieldsToEncrypt as $field) { 24 | if (isset($content[$field])) { 25 | $content[$field] = Crypt::encryptString($content[$field]); 26 | } 27 | } 28 | 29 | $response->setContent(json_encode($content)); 30 | } 31 | 32 | return $response; 33 | } 34 | } -------------------------------------------------------------------------------- /app/Http/Middleware/TeamsPermission.php: -------------------------------------------------------------------------------- 1 | route('login')->with('error', 'You must be logged in to access this area.'); 17 | } 18 | 19 | // Allow staff and admin users to access without team restrictions 20 | if ($user->hasRole(['staff', 'admin'])) { 21 | return $next($request); 22 | } 23 | 24 | if (!$user->currentTeam) { 25 | // Redirect to a default route or show an error 26 | return redirect()->route('home')->with('error', 'You must be part of a team to access this area.'); 27 | } 28 | 29 | // Check if the requested team matches the user's current team 30 | $requestedTeamId = $request->route('tenant'); 31 | if ($requestedTeamId && $requestedTeamId != $user->currentTeam->id) { 32 | return redirect()->route('staff.dashboard', ['tenant' => $user->currentTeam->id]) 33 | ->with('error', 'You do not have permission to access this team.'); 34 | } 35 | 36 | // Check if the user has permission to access the current route 37 | // You can implement your team-based permission logic here 38 | 39 | return $next($request); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrimStrings.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | 'current_password', 16 | 'password', 17 | 'password_confirmation', 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustHosts.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | public function hosts(): array 15 | { 16 | return [ 17 | $this->allSubdomainsOfApplicationUrl(), 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustProxies.php: -------------------------------------------------------------------------------- 1 | |string|null 14 | */ 15 | protected $proxies = '*'; 16 | 17 | /** 18 | * The headers that should be used to detect proxies. 19 | * 20 | * @var int 21 | */ 22 | protected $headers = 23 | Request::HEADER_X_FORWARDED_FOR | 24 | Request::HEADER_X_FORWARDED_HOST | 25 | Request::HEADER_X_FORWARDED_PORT | 26 | Request::HEADER_X_FORWARDED_PROTO | 27 | Request::HEADER_X_FORWARDED_AWS_ELB; 28 | } 29 | -------------------------------------------------------------------------------- /app/Http/Middleware/ValidateSignature.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 'fbclid', 16 | // 'utm_campaign', 17 | // 'utm_content', 18 | // 'utm_medium', 19 | // 'utm_source', 20 | // 'utm_term', 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /app/Http/Middleware/VerifyCsrfToken.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /app/Listeners/CreatePersonalTeam.php: -------------------------------------------------------------------------------- 1 | teamManagementService = $teamManagementService; 15 | } 16 | 17 | public function handle(Registered $event): void 18 | { 19 | $this->teamManagementService->assignUserToDefaultTeam($event->user); 20 | } 21 | } -------------------------------------------------------------------------------- /app/Listeners/DnsSettingSavedListener.php: -------------------------------------------------------------------------------- 1 | dnsSettingResource->updateBindDnsRecord($dnsSetting); 26 | } 27 | } -------------------------------------------------------------------------------- /app/Listeners/EmailTracker.php: -------------------------------------------------------------------------------- 1 | message; 14 | $campaignId = $message->getHeaders()->get('X-Campaign-ID'); 15 | $leadId = $message->getHeaders()->get('X-Lead-ID'); 16 | 17 | if ($campaignId && $leadId) { 18 | $campaign = EmailCampaign::find($campaignId); 19 | $lead = Lead::find($leadId); 20 | 21 | if ($campaign && $lead) { 22 | // Record that the email was sent 23 | $campaign->emailStats()->create([ 24 | 'lead_id' => $lead->id, 25 | 'sent_at' => now(), 26 | ]); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /app/Listeners/SwitchTeam.php: -------------------------------------------------------------------------------- 1 | dnsSetting; 20 | 21 | try { 22 | $domain = $dnsSetting->domain->name; 23 | $recordType = $dnsSetting->record_type; 24 | $recordName = $dnsSetting->name; 25 | $recordValue = $dnsSetting->value; 26 | $ttl = $dnsSetting->ttl; 27 | 28 | $zoneFile = "./bind/records/{$domain}.db"; 29 | 30 | if ($recordType === 'A') { 31 | $recordLine = "{$recordName} {$ttl} IN A {$recordValue}"; 32 | } elseif ($recordType === 'MX') { 33 | $recordLine = "{$domain}. {$ttl} IN MX 10 {$recordValue}."; 34 | } 35 | 36 | file_put_contents($zoneFile, $recordLine . "\n", FILE_APPEND); 37 | 38 | exec('docker-compose restart bind9'); 39 | } catch (\Exception $e) { 40 | Log::error('Failed to update Bind9 DNS records: ' . $e->getMessage()); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /app/Models/AccessLog.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class); 29 | } 30 | } -------------------------------------------------------------------------------- /app/Models/Database.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class); 14 | } 15 | } -------------------------------------------------------------------------------- /app/Models/DnsSetting.php: -------------------------------------------------------------------------------- 1 | 'integer', 40 | ]; 41 | 42 | /** 43 | * Get the domain that owns the DNS setting. 44 | */ 45 | public function domain() 46 | { 47 | return $this->belongsTo(Domain::class); 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /app/Models/EmailAccount.php: -------------------------------------------------------------------------------- 1 | 'array', 35 | ]; 36 | 37 | /** 38 | * Get the user that owns the email account. 39 | */ 40 | public function user() 41 | { 42 | return $this->belongsTo(User::class); 43 | } 44 | 45 | /** 46 | * Get the domain that owns the email account. 47 | */ 48 | public function domain() 49 | { 50 | return $this->belongsTo(Domain::class); 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /app/Models/HostingPlan.php: -------------------------------------------------------------------------------- 1 | 'decimal:2', 39 | ]; 40 | 41 | /** 42 | * The attributes that should be hidden for arrays. 43 | * 44 | * @var array 45 | */ 46 | protected $hidden = [ 47 | 'created_at', 48 | 'updated_at', 49 | ]; 50 | 51 | public function userHostingPlans() 52 | { 53 | return $this->hasMany(UserHostingPlan::class); 54 | } 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /app/Models/Membership.php: -------------------------------------------------------------------------------- 1 | belongsTo(Menu::class, 'parent_id'); 22 | } 23 | 24 | public function children() 25 | { 26 | return $this->hasMany(Menu::class, 'parent_id'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Models/ResourceUsage.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class); 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /app/Models/SiteSettings.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | protected $fillable = [ 17 | 'email', 18 | 'role', 19 | ]; 20 | 21 | /** 22 | * Get the team that the invitation belongs to. 23 | */ 24 | public function team(): BelongsTo 25 | { 26 | return $this->belongsTo(Jetstream::teamModel()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Models/UserHostingPlan.php: -------------------------------------------------------------------------------- 1 | 'date', 38 | 'end_date' => 'date', 39 | ]; 40 | 41 | /** 42 | * Get the user that owns the hosting plan. 43 | */ 44 | public function user() 45 | { 46 | return $this->belongsTo(User::class); 47 | } 48 | 49 | /** 50 | * Get the hosting plan associated with the user. 51 | */ 52 | public function hostingPlan() 53 | { 54 | return $this->belongsTo(HostingPlan::class); 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /app/Policies/ConnectedAccountPolicy.php: -------------------------------------------------------------------------------- 1 | ownsConnectedAccount($connectedAccount); 27 | } 28 | 29 | /** 30 | * Determine whether the user can create models. 31 | */ 32 | public function create(User $user): bool 33 | { 34 | return true; 35 | } 36 | 37 | /** 38 | * Determine whether the user can update the model. 39 | */ 40 | public function update(User $user, ConnectedAccount $connectedAccount): bool 41 | { 42 | return $user->ownsConnectedAccount($connectedAccount); 43 | } 44 | 45 | /** 46 | * Determine whether the user can delete the model. 47 | */ 48 | public function delete(User $user, ConnectedAccount $connectedAccount): bool 49 | { 50 | return $user->ownsConnectedAccount($connectedAccount); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton(DockerComposeService::class, function ($app) { 16 | return new DockerComposeService(); 17 | }); 18 | } 19 | 20 | /** 21 | * Bootstrap any application services. 22 | */ 23 | public function boot(): void 24 | { 25 | // 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $policies = [ 16 | // 17 | ]; 18 | 19 | /** 20 | * Register any authentication / authorization services. 21 | */ 22 | public function boot(): void 23 | { 24 | // 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | dnsSetting = $dnsSetting; 28 | } 29 | } -------------------------------------------------------------------------------- /app/Providers/DockerServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton(DockerComposeService::class, function ($app) { 16 | return new DockerComposeService(); 17 | }); 18 | } 19 | 20 | /** 21 | * Bootstrap services. 22 | */ 23 | public function boot(): void 24 | { 25 | // 26 | } 27 | } -------------------------------------------------------------------------------- /app/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | > 16 | */ 17 | protected $listen = [ 18 | Registered::class => [ 19 | SendEmailVerificationNotification::class, 20 | ], 21 | DnsSetting::class => [ 22 | DnsSettingSavedListener::class, 23 | ], 24 | ]; 25 | 26 | /** 27 | * Register any events for your application. 28 | */ 29 | public function boot(): void 30 | { 31 | // 32 | } 33 | 34 | /** 35 | * Determine if events and listeners should be automatically discovered. 36 | */ 37 | public function shouldDiscoverEvents(): bool 38 | { 39 | return false; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | by($request->user()?->id ?: $request->ip()); 29 | }); 30 | 31 | $this->routes(function () { 32 | Route::middleware('api') 33 | ->prefix('api') 34 | ->group(base_path('routes/api.php')); 35 | 36 | Route::middleware('web') 37 | ->group(base_path('routes/web.php')); 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Providers/SiteSettingsServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton('siteconfig', function () { 16 | return new SiteConfig(); 17 | }); 18 | } 19 | 20 | /** 21 | * Bootstrap services. 22 | */ 23 | public function boot(): void 24 | { 25 | // 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Providers/SocialstreamServiceProvider.php: -------------------------------------------------------------------------------- 1 | getMessage()); 17 | return false; 18 | } 19 | } 20 | 21 | public function modifyDatabase(string $name, string $charset, string $collation): bool 22 | { 23 | try { 24 | DB::statement("ALTER DATABASE `$name` CHARACTER SET $charset COLLATE $collation"); 25 | return true; 26 | } catch (\Exception $e) { 27 | Log::error("Failed to modify database: " . $e->getMessage()); 28 | return false; 29 | } 30 | } 31 | 32 | public function dropDatabase(string $name): bool 33 | { 34 | try { 35 | DB::statement("DROP DATABASE `$name`"); 36 | return true; 37 | } catch (\Exception $e) { 38 | Log::error("Failed to drop database: " . $e->getMessage()); 39 | return false; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /app/Services/RestoreService.php: -------------------------------------------------------------------------------- 1 | file_path; 16 | 17 | // Decrypt the backup file 18 | $decryptedContent = Crypt::decryptString(file_get_contents($backupPath)); 19 | $tempPath = storage_path('app/temp_backup.zip'); 20 | file_put_contents($tempPath, $decryptedContent); 21 | 22 | $zip = new \ZipArchive(); 23 | $zip->open($tempPath); 24 | 25 | // Restore database 26 | $databaseDump = $zip->getFromName('database.sql'); 27 | $this->restoreDatabase($databaseDump); 28 | 29 | // Restore storage folder 30 | $zip->extractTo(storage_path('app'), 'storage'); 31 | 32 | $zip->close(); 33 | 34 | // Clean up temporary file 35 | File::delete($tempPath); 36 | } 37 | 38 | private function restoreDatabase($databaseDump) 39 | { 40 | // Implement database restore logic here 41 | // You may use Laravel's database configuration to get connection details 42 | // and execute the SQL statements from the database dump 43 | DB::unprepared($databaseDump); 44 | } 45 | } -------------------------------------------------------------------------------- /app/Services/SftpService.php: -------------------------------------------------------------------------------- 1 | sftp = new SFTP($domain->domain_name, 2222); 15 | if (!$this->sftp->login($domain->sftp_username, $domain->sftp_password)) { 16 | throw new \Exception('Login failed'); 17 | } 18 | } 19 | 20 | public function listFiles($directory = '.') 21 | { 22 | return $this->sftp->nlist($directory); 23 | } 24 | 25 | public function uploadFile($localFile, $remoteFile) 26 | { 27 | return $this->sftp->put($remoteFile, $localFile, SFTP::SOURCE_LOCAL_FILE); 28 | } 29 | 30 | public function downloadFile($remoteFile, $localFile) 31 | { 32 | return $this->sftp->get($remoteFile, $localFile); 33 | } 34 | 35 | public function deleteFile($remoteFile) 36 | { 37 | return $this->sftp->delete($remoteFile); 38 | } 39 | 40 | public function createDirectory($directory) 41 | { 42 | return $this->sftp->mkdir($directory); 43 | } 44 | 45 | public function removeDirectory($directory) 46 | { 47 | return $this->sftp->rmdir($directory); 48 | } 49 | } -------------------------------------------------------------------------------- /app/View/Components/AppLayout.php: -------------------------------------------------------------------------------- 1 | ['api/*', 'sanctum/csrf-cookie'], 19 | 20 | 'allowed_methods' => ['*'], 21 | 22 | 'allowed_origins' => ['*'], 23 | 24 | 'allowed_origins_patterns' => [], 25 | 26 | 'allowed_headers' => ['*'], 27 | 28 | 'exposed_headers' => [], 29 | 30 | 'max_age' => 0, 31 | 32 | 'supports_credentials' => false, 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 21 | 'scheme' => 'https', 22 | ], 23 | 24 | 'postmark' => [ 25 | 'token' => env('POSTMARK_TOKEN'), 26 | ], 27 | 28 | 'ses' => [ 29 | 'key' => env('AWS_ACCESS_KEY_ID'), 30 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 31 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 32 | ], 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /config/socialstream.php: -------------------------------------------------------------------------------- 1 | ['web'], 7 | 'prompt' => 'Or Login Via', 8 | 'providers' => [ 9 | // Providers::github(), 10 | ], 11 | 'component' => 'socialstream::components.socialstream', 12 | ]; 13 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | resource_path('views'), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => env( 32 | 'VIEW_COMPILED_PATH', 33 | realpath(storage_path('framework/views')) 34 | ), 35 | 36 | ]; 37 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /database/factories/ConnectedAccountFactory.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class ConnectedAccountFactory extends Factory 14 | { 15 | protected $model = ConnectedAccount::class; 16 | 17 | public function definition(): array 18 | { 19 | return [ 20 | 'provider' => $this->faker->randomElement(Providers::all()), 21 | 'provider_id' => $this->faker->numerify('########'), 22 | 'token' => Str::random(432), 23 | 'refresh_token' => Str::random(432), 24 | ]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /database/factories/DnsSettingFactory.php: -------------------------------------------------------------------------------- 1 | function () { 28 | return \App\Models\Domain::factory()->create()->id; 29 | }, 30 | 'record_type' => $this->faker->randomElement($recordTypes), 31 | 'name' => $this->faker->domainName, 32 | 'value' => $this->faker->ipv4, 33 | 'ttl' => $this->faker->numberBetween(300, 86400), // TTL between 300 seconds (5 minutes) and 86400 seconds (24 hours) 34 | 'created_at' => now(), 35 | 'updated_at' => now(), 36 | ]; 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /database/factories/EmailAccountFactory.php: -------------------------------------------------------------------------------- 1 | function () { 26 | return \App\Models\User::factory()->create()->id; 27 | }, 28 | 'domain_id' => function () { 29 | return \App\Models\Domain::factory()->create()->id; 30 | }, 31 | 'email_address' => $this->faker->unique()->safeEmail, 32 | 'password' => bcrypt('password'), // Replace 'password' with your desired default password generation logic 33 | 'quota' => $this->faker->numberBetween(100, 5000), // Quota between 100 MB to 5000 MB 34 | 'created_at' => now(), 35 | 'updated_at' => now(), 36 | ]; 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /database/factories/HostingPlanFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->words(3, true), // Generates a name consisting of 3 words 26 | 'description' => $this->faker->paragraph(), 27 | 'disk_space' => $this->faker->numberBetween(100, 10000), // Generates disk space between 100 MB to 10,000 MB 28 | 'bandwidth' => $this->faker->numberBetween(100, 10000), // Generates bandwidth between 100 MB to 10,000 MB 29 | 'price' => $this->faker->randomFloat(2, 10, 200), // Generates a price between 10 and 200 with up to 2 decimal places 30 | 'created_at' => now(), 31 | 'updated_at' => now(), 32 | ]; 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /database/factories/MenuFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->word, 16 | 'url' => $this->faker->url, 17 | 'order' => $this->faker->numberBetween(1, 10), 18 | ]; 19 | } 20 | } -------------------------------------------------------------------------------- /database/factories/ResourceUsageFactory.php: -------------------------------------------------------------------------------- 1 | function () { 26 | return \App\Models\User::factory()->create()->id; 27 | }, 28 | 'disk_usage' => $this->faker->numberBetween(100, 5000), // Disk usage between 100 MB to 5000 MB 29 | 'bandwidth_usage' => $this->faker->numberBetween(100, 10000), // Bandwidth usage between 100 MB to 10,000 MB 30 | 'month' => $this->faker->numberBetween(1, 12), // Random month between 1 (January) to 12 (December) 31 | 'year' => $this->faker->numberBetween(2020, 2023), // Random year between 2020 to 2023 32 | 'created_at' => now(), 33 | 'updated_at' => now(), 34 | ]; 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /database/factories/TeamFactory.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class TeamFactory extends Factory 12 | { 13 | /** 14 | * Define the model's default state. 15 | * 16 | * @return array 17 | */ 18 | public function definition(): array 19 | { 20 | return [ 21 | 'name' => $this->faker->unique()->company(), 22 | 'user_id' => User::factory(), 23 | 'personal_team' => true, 24 | ]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /database/factories/UserHostingPlanFactory.php: -------------------------------------------------------------------------------- 1 | function () { 26 | return \App\Models\User::factory()->create()->id; 27 | }, 28 | 'hosting_plan_id' => function () { 29 | return \App\Models\HostingPlan::factory()->create()->id; 30 | }, 31 | 'start_date' => $this->faker->dateTimeBetween('-1 year', 'now'), 32 | 'end_date' => $this->faker->dateTimeBetween('now', '+1 year'), 33 | 'created_at' => now(), 34 | 'updated_at' => now(), 35 | ]; 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000001_make_password_nullable_on_users_table.php: -------------------------------------------------------------------------------- 1 | string('password')->nullable()->change(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('users', function (Blueprint $table) { 25 | $table->string('password')->nullable(false)->change(); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000002_create_connected_accounts_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->foreignId('user_id'); 17 | $table->string('provider'); 18 | $table->string('provider_id'); 19 | $table->string('name')->nullable(); 20 | $table->string('nickname')->nullable(); 21 | $table->string('email')->nullable(); 22 | $table->string('telephone')->nullable(); 23 | $table->text('avatar_path')->nullable(); 24 | $table->string('token', 1000); 25 | $table->string('secret')->nullable(); // OAuth1 26 | $table->string('refresh_token', 1000)->nullable(); // OAuth2 27 | $table->dateTime('expires_at')->nullable(); // OAuth2 28 | $table->timestamps(); 29 | 30 | $table->index(['user_id', 'id']); 31 | $table->index(['provider', 'provider_id']); 32 | }); 33 | } 34 | 35 | /** 36 | * Reverse the migrations. 37 | */ 38 | public function down(): void 39 | { 40 | Schema::dropIfExists('connected_accounts'); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_200000_add_two_factor_columns_to_users_table.php: -------------------------------------------------------------------------------- 1 | text('two_factor_secret') 17 | ->after('password') 18 | ->nullable(); 19 | 20 | $table->text('two_factor_recovery_codes') 21 | ->after('two_factor_secret') 22 | ->nullable(); 23 | 24 | if (Fortify::confirmsTwoFactorAuthentication()) { 25 | $table->timestamp('two_factor_confirmed_at') 26 | ->after('two_factor_recovery_codes') 27 | ->nullable(); 28 | } 29 | }); 30 | } 31 | 32 | /** 33 | * Reverse the migrations. 34 | */ 35 | public function down(): void 36 | { 37 | Schema::table('users', function (Blueprint $table) { 38 | $table->dropColumn(array_merge([ 39 | 'two_factor_secret', 40 | 'two_factor_recovery_codes', 41 | ], Fortify::confirmsTwoFactorAuthentication() ? [ 42 | 'two_factor_confirmed_at', 43 | ] : [])); 44 | }); 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /database/migrations/2019_08_19_000000_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('uuid')->unique(); 17 | $table->text('connection'); 18 | $table->text('queue'); 19 | $table->longText('payload'); 20 | $table->longText('exception'); 21 | $table->timestamp('failed_at')->useCurrent(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::dropIfExists('failed_jobs'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/2020_05_21_100000_create_teams_table.php: -------------------------------------------------------------------------------- 1 | id(); 15 | $table->foreignId('user_id')->index(); 16 | $table->string('name'); 17 | $table->boolean('personal_team'); 18 | $table->timestamps(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | */ 25 | public function down(): void 26 | { 27 | Schema::dropIfExists('teams'); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/migrations/2020_05_21_200000_create_team_user_table.php: -------------------------------------------------------------------------------- 1 | id(); 15 | $table->foreignId('team_id'); 16 | $table->foreignId('user_id'); 17 | $table->string('role')->nullable(); 18 | $table->timestamps(); 19 | 20 | $table->unique(['team_id', 'user_id']); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | */ 27 | public function down(): void 28 | { 29 | Schema::dropIfExists('team_user'); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /database/migrations/2020_05_21_300000_create_team_invitations_table.php: -------------------------------------------------------------------------------- 1 | id(); 15 | $table->foreignId('team_id')->constrained()->cascadeOnDelete(); 16 | $table->string('email'); 17 | $table->string('role')->nullable(); 18 | $table->string('token', 64)->unique(); 19 | $table->timestamps(); 20 | 21 | $table->unique(['team_id', 'email']); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::dropIfExists('team_invitations'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/2023_06_11_000000_create_databases_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('name'); 17 | $table->string('charset')->default('utf8mb4'); 18 | $table->string('collation')->default('utf8mb4_unicode_ci'); 19 | $table->foreignId('user_id')->constrained('users')->onDelete('cascade'); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | */ 27 | public function down(): void 28 | { 29 | Schema::dropIfExists('databases'); 30 | } 31 | }; -------------------------------------------------------------------------------- /database/migrations/2024_06_13_125458_create_domains_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->unsignedBigInteger('user_id'); 19 | $table->string('domain_name')->unique(); 20 | $table->date('registration_date'); 21 | $table->date('expiration_date'); 22 | $table->timestamps(); // This will create the 'created_at' and 'updated_at' columns 23 | 24 | // Foreign key constraint 25 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function down() 35 | { 36 | Schema::dropIfExists('domains'); 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /database/migrations/2024_06_14_125614_create_dns_settings_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->unsignedBigInteger('domain_id'); 19 | $table->string('record_type'); // e.g., A, CNAME, MX, TXT 20 | $table->string('name'); 21 | $table->string('value'); 22 | $table->integer('ttl'); // time-to-live 23 | $table->timestamps(); // This will create the 'created_at' and 'updated_at' columns 24 | 25 | // Foreign key constraint 26 | $table->foreign('domain_id')->references('id')->on('domains')->onDelete('cascade'); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::dropIfExists('dns_settings'); 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /database/migrations/2024_06_15_125159_create_hosting_plans_table.php: -------------------------------------------------------------------------------- 1 | id(); 17 | $table->string('name'); 18 | $table->text('description')->nullable(); 19 | $table->integer('disk_space'); // in MB 20 | $table->integer('bandwidth'); // in MB 21 | $table->decimal('price', 8, 2); // Assuming price up to 999,999.99 22 | $table->timestamps(); // This will create the 'created_at' and 'updated_at' columns 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('hosting_plans'); 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /database/migrations/2024_06_15_125338_create_user_hosting_plans_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->unsignedBigInteger('user_id'); 19 | $table->unsignedBigInteger('hosting_plan_id'); 20 | $table->date('start_date'); 21 | $table->date('end_date')->nullable(); 22 | $table->timestamps(); // This will create the 'created_at' and 'updated_at' columns 23 | 24 | // Foreign key constraints 25 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 26 | $table->foreign('hosting_plan_id')->references('id')->on('hosting_plans')->onDelete('cascade'); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::dropIfExists('user_hosting_plans'); 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /database/migrations/2024_06_15_125614_add_priority_to_dns_settings_table.php: -------------------------------------------------------------------------------- 1 | integer('priority')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('dns_settings', function (Blueprint $table) { 29 | $table->dropColumn('priority'); 30 | }); 31 | } 32 | } -------------------------------------------------------------------------------- /database/migrations/2024_06_15_125713_create_email_accounts_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->unsignedBigInteger('user_id'); 19 | $table->unsignedBigInteger('domain_id'); 20 | $table->string('email_address')->unique(); 21 | $table->string('password'); 22 | $table->integer('quota'); // in MB 23 | $table->timestamps(); // This will create the 'created_at' and 'updated_at' columns 24 | 25 | // Foreign key constraints 26 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 27 | $table->foreign('domain_id')->references('id')->on('domains')->onDelete('cascade'); 28 | }); 29 | } 30 | 31 | /** 32 | * Reverse the migrations. 33 | * 34 | * @return void 35 | */ 36 | public function down() 37 | { 38 | Schema::dropIfExists('email_accounts'); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /database/migrations/2024_06_15_125818_create_resource_usage_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->unsignedBigInteger('user_id'); 19 | $table->integer('disk_usage'); // in MB 20 | $table->integer('bandwidth_usage'); // in MB 21 | $table->integer('month'); 22 | $table->integer('year'); 23 | $table->timestamps(); // This will create the 'created_at' and 'updated_at' columns 24 | 25 | // Foreign key constraint 26 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::dropIfExists('resource_usage'); 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /database/migrations/2024_06_16_000000_add_hosting_plan_id_to_domains_table.php: -------------------------------------------------------------------------------- 1 | foreignId('hosting_plan_id')->constrained('user_hosting_plans')->onDelete('cascade'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::dropIfExists('domains'); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /database/migrations/2024_07_24_080000_create_menus_table.php: -------------------------------------------------------------------------------- 1 | id(); 13 | $table->string('name'); 14 | $table->string('url'); 15 | $table->unsignedBigInteger('parent_id')->nullable(); 16 | $table->integer('order')->default(0); 17 | $table->timestamps(); 18 | $table->softDeletes(); 19 | 20 | $table->foreign('parent_id')->references('id')->on('menus')->onDelete('cascade'); 21 | }); 22 | } 23 | 24 | public function down(): void 25 | { 26 | Schema::dropIfExists('menus'); 27 | } 28 | }; -------------------------------------------------------------------------------- /database/migrations/2024_07_25_000000_add_sftp_ssh_credentials_to_domains_table.php: -------------------------------------------------------------------------------- 1 | string('sftp_username')->nullable(); 15 | $table->string('sftp_password')->nullable(); 16 | $table->string('ssh_username')->nullable(); 17 | $table->string('ssh_password')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('domains', function (Blueprint $table) { 27 | $table->dropColumn(['sftp_username', 'sftp_password', 'ssh_username', 'ssh_password']); 28 | }); 29 | } 30 | }; -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | 'Admin User', 21 | 'email' => 'admin@example.com', 22 | 'password' => Hash::make('password'), 23 | 'email_verified_at' => now(), 24 | ]); 25 | 26 | $this->call([ 27 | HostingPlanSeeder::class, 28 | // SiteSettingsSeeder::class, 29 | ]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/seeders/HostingPlanSeeder.php: -------------------------------------------------------------------------------- 1 | 'Free', 19 | 'description' => 'Free hosting plan with limited resources', 20 | 'disk_space' => 1000, // 1 GB 21 | 'bandwidth' => 10000, // 10 GB 22 | 'price' => 0.00, 23 | ]); 24 | 25 | HostingPlan::create([ 26 | 'name' => 'Premium', 27 | 'description' => 'Premium hosting plan with enhanced resources', 28 | 'disk_space' => 10000, // 10 GB 29 | 'bandwidth' => 100000, // 100 GB 30 | 'price' => 9.99, 31 | ]); 32 | } 33 | } -------------------------------------------------------------------------------- /database/seeders/PermissionsSeeder.php: -------------------------------------------------------------------------------- 1 | 'admin']); 17 | $permissions = Permission::where('guard_name', 'web')->pluck('id')->toArray(); 18 | $adminRole->syncPermissions($permissions); 19 | 20 | $freeRole = Role::firstOrCreate(['name' => 'free']); 21 | $freePermissions = Permission::where('guard_name', 'web')->pluck('id')->toArray(); 22 | $freeRole->syncPermissions($freePermissions); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /database/seeders/SiteSettingsSeeder.php: -------------------------------------------------------------------------------- 1 | config('app.name', 'Liberu '), 14 | 'currency' => '£', 15 | 'default_language' => 'en', 16 | 'address' => '123 St, London, UK', 17 | 'country' => 'United Kingdom', 18 | 'email' => 'info@liberurealestate.com', 19 | 'phone_01' => '+44 123 456 7890', 20 | 'phone_02' => '+44 123 456 7890', 21 | 'phone_03' => '+44 123 456 7890', 22 | 'phone_04' => '+44 123 456 7890', 23 | 'facebook' => 'https://facebook.com/liberusoftware', 24 | 'twitter' => 'https://twitter.com/liberusoftware', 25 | 'github' => 'https://Github.com/liberusoftware', 26 | 'youtube' => 'https://YouTube.com/@liberusoftware', 27 | ]; 28 | 29 | foreach ($data as $key => $value) { 30 | SiteConfig::store($key, $value); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /docker-compose.secrets.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | secrets: 4 | db_root_password: 5 | file: ./secrets/db_root_password.txt 6 | db_password: 7 | file: ./secrets/db_password.txt 8 | 9 | services: 10 | # Your existing services will be defined here -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build" 7 | }, 8 | "devDependencies": { 9 | "@tailwindcss/forms": "^0.5.9", 10 | "@tailwindcss/typography": "^0.5.10", 11 | "autoprefixer": "^10.4.16", 12 | "laravel-vite-plugin": "^1.3.0", 13 | "postcss": "^8.4.32", 14 | "postcss-nesting": "^13.0.0", 15 | "tailwindcss": "^3.4.0", 16 | "vite": "^6.3.5" 17 | }, 18 | "dependencies": { 19 | "vite-plugin-static-copy": "^3.0.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | tests/Unit 10 | 11 | 12 | tests/Feature 13 | 14 | 15 | 16 | 17 | app 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | 'tailwindcss/nesting': 'postcss-nesting', 4 | tailwindcss: {}, 5 | autoprefixer: {}, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /public/build/assets/app-l0sNRNKZ.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/build/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-control-panel/control-panel-laravel/9a25e557cbddb15c4bbe051663d650f0b336a78d/public/build/images/logo.png -------------------------------------------------------------------------------- /public/build/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources/css/app.css": { 3 | "file": "assets/app-DtDIA-zY.css", 4 | "src": "resources/css/app.css", 5 | "isEntry": true 6 | }, 7 | "resources/css/filament/admin/theme.css": { 8 | "file": "assets/theme-BA7Me4im.css", 9 | "src": "resources/css/filament/admin/theme.css", 10 | "isEntry": true 11 | }, 12 | "resources/js/app.js": { 13 | "file": "assets/app-l0sNRNKZ.js", 14 | "name": "app", 15 | "src": "resources/js/app.js", 16 | "isEntry": true 17 | } 18 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-control-panel/control-panel-laravel/9a25e557cbddb15c4bbe051663d650f0b336a78d/public/favicon.ico -------------------------------------------------------------------------------- /public/js/filament/forms/components/key-value.js: -------------------------------------------------------------------------------- 1 | function r({state:o}){return{state:o,rows:[],shouldUpdateRows:!0,init:function(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(t,e)=>{let s=i=>i===null?0:Array.isArray(i)?i.length:typeof i!="object"?0:Object.keys(i).length;s(t)===0&&s(e)===0||this.updateRows()})},addRow:function(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow:function(t){this.rows.splice(t,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows:function(t){let e=Alpine.raw(this.rows);this.rows=[];let s=e.splice(t.oldIndex,1)[0];e.splice(t.newIndex,0,s),this.$nextTick(()=>{this.rows=e,this.updateState()})},updateRows:function(){if(!this.shouldUpdateRows){this.shouldUpdateRows=!0;return}let t=[];for(let[e,s]of Object.entries(this.state??{}))t.push({key:e,value:s});this.rows=t},updateState:function(){let t={};this.rows.forEach(e=>{e.key===""||e.key===null||(t[e.key]=e.value)}),this.shouldUpdateRows=!1,this.state=t}}}export{r as default}; 2 | -------------------------------------------------------------------------------- /public/js/filament/forms/components/tags-input.js: -------------------------------------------------------------------------------- 1 | function i({state:a,splitKeys:n}){return{newTag:"",state:a,createTag:function(){if(this.newTag=this.newTag.trim(),this.newTag!==""){if(this.state.includes(this.newTag)){this.newTag="";return}this.state.push(this.newTag),this.newTag=""}},deleteTag:function(t){this.state=this.state.filter(e=>e!==t)},reorderTags:function(t){let e=this.state.splice(t.oldIndex,1)[0];this.state.splice(t.newIndex,0,e),this.state=[...this.state]},input:{"x-on:blur":"createTag()","x-model":"newTag","x-on:keydown"(t){["Enter",...n].includes(t.key)&&(t.preventDefault(),t.stopPropagation(),this.createTag())},"x-on:paste"(){this.$nextTick(()=>{if(n.length===0){this.createTag();return}let t=n.map(e=>e.replace(/[/\-\\^$*+?.()|[\]{}]/g,"\\$&")).join("|");this.newTag.split(new RegExp(t,"g")).forEach(e=>{this.newTag=e,this.createTag()})})}}}}export{i as default}; 2 | -------------------------------------------------------------------------------- /public/js/filament/forms/components/textarea.js: -------------------------------------------------------------------------------- 1 | function r({initialHeight:t,shouldAutosize:i,state:s}){return{state:s,wrapperEl:null,init:function(){this.wrapperEl=this.$el.parentNode,this.setInitialHeight(),i?this.$watch("state",()=>{this.resize()}):this.setUpResizeObserver()},setInitialHeight:function(){this.$el.scrollHeight<=0||(this.wrapperEl.style.height=t+"rem")},resize:function(){if(this.setInitialHeight(),this.$el.scrollHeight<=0)return;let e=this.$el.scrollHeight+"px";this.wrapperEl.style.height!==e&&(this.wrapperEl.style.height=e)},setUpResizeObserver:function(){new ResizeObserver(()=>{this.wrapperEl.style.height=this.$el.style.height}).observe(this.$el)}}}export{r as default}; 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /resources/css/filament/admin/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import preset from '../../../../vendor/filament/filament/tailwind.config.preset' 2 | 3 | export default { 4 | presets: [preset], 5 | content: [ 6 | './app/Filament/**/*.php', 7 | './resources/views/**/*.blade.php', 8 | './resources/views/filament/**/*.blade.php', 9 | './vendor/filament/**/*.blade.php', 10 | './vendor/laravel/jetstream/**/*.blade.php', 11 | ], 12 | } 13 | -------------------------------------------------------------------------------- /resources/css/filament/admin/theme.css: -------------------------------------------------------------------------------- 1 | @import '/vendor/filament/filament/resources/css/theme.css'; 2 | 3 | @config 'tailwind.config.js'; 4 | 5 | /* .fi-body { 6 | background-color: #ebebeb !important; 7 | } 8 | 9 | .fi-sidebar { 10 | background-color: #292828 !important; 11 | } */ -------------------------------------------------------------------------------- /resources/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-control-panel/control-panel-laravel/9a25e557cbddb15c4bbe051663d650f0b336a78d/resources/images/logo.png -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-control-panel/control-panel-laravel/9a25e557cbddb15c4bbe051663d650f0b336a78d/resources/js/app.js -------------------------------------------------------------------------------- /resources/markdown/policy.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | Edit this file to define the privacy policy for your application. 4 | -------------------------------------------------------------------------------- /resources/markdown/terms.md: -------------------------------------------------------------------------------- 1 | # Terms of Service 2 | 3 | Edit this file to define the terms of service for your application. 4 | -------------------------------------------------------------------------------- /resources/views/api/index.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | {{ __('API Tokens') }} 5 |

6 |
7 | 8 |
9 |
10 | @livewire('api.api-token-manager') 11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /resources/views/auth/confirm-password.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | {{ __('This is a secure area of the application. Please confirm your password before continuing.') }} 9 |
10 | 11 | 12 | 13 |
14 | @csrf 15 | 16 |
17 | 18 | 19 |
20 | 21 |
22 | 23 | {{ __('Confirm') }} 24 | 25 |
26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /resources/views/components/action-link.blade.php: -------------------------------------------------------------------------------- 1 | merge(['class' => 'inline-flex items-center px-4 py-2 bg-white border border-gray-800 rounded-md font-semibold text-xs text-gray-800 uppercase tracking-widest hover:bg-gray-200 hover:border-gray-600 active:border-gray-900 focus:outline-none focus:border-gray-900 focus:shadow-outline-gray disabled:opacity-25 transition ease-in-out duration-150'])}}> 2 | {{ $slot }} 3 | 4 | -------------------------------------------------------------------------------- /resources/views/components/action-message.blade.php: -------------------------------------------------------------------------------- 1 | @props(['on']) 2 | 3 |
merge(['class' => 'text-sm text-gray-600']) }}> 9 | {{ $slot->isEmpty() ? 'Saved.' : $slot }} 10 |
11 | -------------------------------------------------------------------------------- /resources/views/components/action-section.blade.php: -------------------------------------------------------------------------------- 1 |
merge(['class' => 'md:grid md:grid-cols-3 md:gap-6']) }}> 2 | 3 | {{ $title }} 4 | {{ $description }} 5 | 6 | 7 |
8 |
9 | {{ $content }} 10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /resources/views/components/application-mark.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/views/components/authentication-card-logo.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/views/components/authentication-card.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{ $logo }} 4 |
5 | 6 |
7 | {{ $slot }} 8 |
9 |
10 | -------------------------------------------------------------------------------- /resources/views/components/button.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /resources/views/components/buttons.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
7 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /resources/views/components/danger-button.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /resources/views/components/dialog-modal.blade.php: -------------------------------------------------------------------------------- 1 | @props(['id' => null, 'maxWidth' => null]) 2 | 3 | 4 |
5 |
6 | {{ $title }} 7 |
8 | 9 |
10 | {{ $content }} 11 |
12 |
13 | 14 |
15 | {{ $footer }} 16 |
17 |
18 | -------------------------------------------------------------------------------- /resources/views/components/dropdown-link.blade.php: -------------------------------------------------------------------------------- 1 | merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out']) }}>{{ $slot }} 2 | -------------------------------------------------------------------------------- /resources/views/components/form-section.blade.php: -------------------------------------------------------------------------------- 1 | @props(['submit']) 2 | 3 |
merge(['class' => 'md:grid md:grid-cols-3 md:gap-6']) }}> 4 | 5 | {{ $title }} 6 | {{ $description }} 7 | 8 | 9 |
10 |
11 |
12 |
13 | {{ $form }} 14 |
15 |
16 | 17 | @if (isset($actions)) 18 |
19 | {{ $actions }} 20 |
21 | @endif 22 |
23 |
24 |
25 | -------------------------------------------------------------------------------- /resources/views/components/home-header.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 25 |
26 | -------------------------------------------------------------------------------- /resources/views/components/input-error.blade.php: -------------------------------------------------------------------------------- 1 | @props(['for']) 2 | 3 | @error($for) 4 |

merge(['class' => 'text-sm text-red-600']) }}>{{ $message }}

5 | @enderror 6 | -------------------------------------------------------------------------------- /resources/views/components/input.blade.php: -------------------------------------------------------------------------------- 1 | @props(['disabled' => false]) 2 | 3 | merge(['class' => 'border-gray-300 focus:border-indigo-500 text-gray-700 focus:ring-indigo-500 rounded-md shadow-sm']) !!}> 4 | -------------------------------------------------------------------------------- /resources/views/components/label.blade.php: -------------------------------------------------------------------------------- 1 | @props(['value']) 2 | 3 | 6 | -------------------------------------------------------------------------------- /resources/views/components/layouts/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {{ config('app.name') }} 11 | 12 | 17 | 18 | @filamentStyles 19 | @vite('resources/css/app.css') 20 | 21 | 22 | 23 | {{ $slot }} 24 | 25 | @filamentScripts 26 | @vite('resources/js/app.js') 27 | 28 | 29 | -------------------------------------------------------------------------------- /resources/views/components/manage_section.blade.php: -------------------------------------------------------------------------------- 1 |
2 |

Manage Your Genealogy

3 |

Explore and manage your family tree with ease.

4 |
5 |
6 |
7 |
Family Tree
8 |

View and edit your family tree.

9 |
10 |
11 |
12 |
13 |
Records
14 |

Access and manage historical records.

15 |
16 |
17 |
18 |
19 |
Settings
20 |

Configure your account and preferences.

21 |
22 |
23 |
24 |
25 | -------------------------------------------------------------------------------- /resources/views/components/nav-link.blade.php: -------------------------------------------------------------------------------- 1 | @props(['active']) 2 | 3 | @php 4 | $classes = ($active ?? false) 5 | ? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out' 6 | : 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out'; 7 | @endphp 8 | 9 | merge(['class' => $classes]) }}> 10 | {{ $slot }} 11 | 12 | -------------------------------------------------------------------------------- /resources/views/components/products_section.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Our Products

4 |

Discover our range of genealogy tools designed to help you explore your ancestry.

5 |
6 | @foreach($products as $product) 7 |
8 | {{ $product->name }} 9 |
10 |

{{ $product->name }}

11 |

{{ $product->description }}

12 | 13 |
14 |
15 | @endforeach 16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /resources/views/components/responsive-nav-link.blade.php: -------------------------------------------------------------------------------- 1 | @props(['active']) 2 | 3 | @php 4 | $classes = ($active ?? false) 5 | ? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 text-start text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out' 6 | : 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out'; 7 | @endphp 8 | 9 | merge(['class' => $classes]) }}> 10 | {{ $slot }} 11 | 12 | -------------------------------------------------------------------------------- /resources/views/components/secondary-button.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /resources/views/components/section-border.blade.php: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /resources/views/components/section-title.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ $title }}

4 | 5 |

6 | {{ $description }} 7 |

8 |
9 | 10 |
11 | {{ $aside ?? '' }} 12 |
13 |
14 | -------------------------------------------------------------------------------- /resources/views/components/socialstream-icons/bitbucket.blade.php: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /resources/views/components/socialstream-icons/facebook.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /resources/views/components/socialstream-icons/github.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /resources/views/components/socialstream-icons/gitlab.blade.php: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /resources/views/components/socialstream-icons/google.blade.php: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /resources/views/components/socialstream-icons/linkedin.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/views/components/socialstream-icons/slack.blade.php: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /resources/views/components/socialstream-icons/twitter.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/components/socialstream.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @if(! empty(\JoelButcher\Socialstream\Socialstream::providers())) 3 |
4 |
5 | 6 | {{ config('socialstream.prompt', 'Or Login Via') }} 7 | 8 |
9 |
10 | @endif 11 | 12 | 13 | 14 |
15 | @foreach (\JoelButcher\Socialstream\Socialstream::providers() as $provider) 16 | 18 | 19 | {{ $provider['buttonLabel'] }} 20 | 21 | @endforeach 22 |
23 |
24 | -------------------------------------------------------------------------------- /resources/views/components/switchable-team.blade.php: -------------------------------------------------------------------------------- 1 | @props(['team', 'component' => 'dropdown-link']) 2 | 3 |
4 | @method('PUT') 5 | @csrf 6 | 7 | 8 | 9 | 10 | 11 |
12 | @if (Auth::user()->isCurrentTeam($team)) 13 | 14 | 15 | 16 | @endif 17 | 18 |
{{ $team->name }}
19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /resources/views/components/validation-errors.blade.php: -------------------------------------------------------------------------------- 1 | @if ($errors->any()) 2 |
3 |
{{ __('Whoops! Something went wrong.') }}
4 | 5 | 10 |
11 | @endif 12 | -------------------------------------------------------------------------------- /resources/views/components/why_us_section.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ $title }}

4 |

{{ $description }}

5 |
6 | @foreach($features as $feature) 7 |
8 | {{ $feature['title'] }} 9 |
10 |

{{ $feature['title'] }}

11 |

{{ $feature['description'] }}

12 |
13 |
14 | @endforeach 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /resources/views/emails/team-invitation.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message') 2 | {{ __('You have been invited to join the :team team!', ['team' => $invitation->team->name]) }} 3 | 4 | @if (Laravel\Fortify\Features::enabled(Laravel\Fortify\Features::registration())) 5 | {{ __('If you do not have an account, you may create one by clicking the button below. After creating an account, you may click the invitation acceptance button in this email to accept the team invitation:') }} 6 | 7 | @component('mail::button', ['url' => route('register')]) 8 | {{ __('Create Account') }} 9 | @endcomponent 10 | 11 | {{ __('If you already have an account, you may accept this invitation by clicking the button below:') }} 12 | 13 | @else 14 | {{ __('You may accept this invitation by clicking the button below:') }} 15 | @endif 16 | 17 | 18 | @component('mail::button', ['url' => $acceptUrl]) 19 | {{ __('Accept Invitation') }} 20 | @endcomponent 21 | 22 | {{ __('If you did not expect to receive an invitation to this team, you may discard this email.') }} 23 | @endcomponent 24 | -------------------------------------------------------------------------------- /resources/views/filament/filament/pages/api-tokens.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | @livewire(\Laravel\Jetstream\Http\Livewire\ApiTokenManager::class) 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/views/filament/filament/pages/create-team.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | @livewire(\Laravel\Jetstream\Http\Livewire\CreateTeamForm::class) 4 |
5 | 6 |
7 | -------------------------------------------------------------------------------- /resources/views/filament/filament/pages/edit-profile.blade.php: -------------------------------------------------------------------------------- 1 | 2 | @if (Laravel\Fortify\Features::canUpdateProfileInformation()) 3 | @livewire(Laravel\Jetstream\Http\Livewire\UpdateProfileInformationForm::class) 4 | 5 | 6 | @endif 7 | 8 | @if (Laravel\Fortify\Features::enabled(Laravel\Fortify\Features::updatePasswords())) 9 |
10 | @livewire(Laravel\Jetstream\Http\Livewire\UpdatePasswordForm::class) 11 |
12 | 13 | 14 | @endif 15 | 16 | @if (Laravel\Fortify\Features::canManageTwoFactorAuthentication()) 17 |
18 | @livewire(Laravel\Jetstream\Http\Livewire\TwoFactorAuthenticationForm::class) 19 |
20 | 21 | 22 | @endif 23 | 24 |
25 | @livewire(Laravel\Jetstream\Http\Livewire\LogoutOtherBrowserSessionsForm::class) 26 |
27 | 28 | @if (Laravel\Jetstream\Jetstream::hasAccountDeletionFeatures()) 29 | 30 | 31 |
32 | @livewire(Laravel\Jetstream\Http\Livewire\DeleteUserForm::class) 33 |
34 | @endif 35 |
36 | -------------------------------------------------------------------------------- /resources/views/filament/filament/pages/edit-team.blade.php: -------------------------------------------------------------------------------- 1 | 2 | @livewire(Laravel\Jetstream\Http\Livewire\UpdateTeamNameForm::class, compact('team')) 3 | 4 | @livewire(Laravel\Jetstream\Http\Livewire\TeamMemberManager::class, compact('team')) 5 | 6 | @if (Gate::check('delete', $team) && ! $team->personal_team) 7 | 8 | 9 | @livewire(Laravel\Jetstream\Http\Livewire\DeleteTeamForm::class, compact('team')) 10 | @endif 11 | 12 | -------------------------------------------------------------------------------- /resources/views/filament/pages/api-tokens.blade.php: -------------------------------------------------------------------------------- 1 | 2 | @livewire(Laravel\Jetstream\Http\Livewire\ApiTokenManager::class) 3 | 4 | -------------------------------------------------------------------------------- /resources/views/filament/pages/create-team.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/views/filament/pages/edit-profile.blade.php: -------------------------------------------------------------------------------- 1 | 2 | @if (Laravel\Fortify\Features::canUpdateProfileInformation()) 3 | @livewire(Laravel\Jetstream\Http\Livewire\UpdateProfileInformationForm::class) 4 | 5 | 6 | @endif 7 | 8 | @if (Laravel\Fortify\Features::enabled(Laravel\Fortify\Features::updatePasswords())) 9 |
10 | @livewire(Laravel\Jetstream\Http\Livewire\UpdatePasswordForm::class) 11 |
12 | 13 | 14 | @endif 15 | 16 | @if (Laravel\Fortify\Features::canManageTwoFactorAuthentication()) 17 |
18 | @livewire(Laravel\Jetstream\Http\Livewire\TwoFactorAuthenticationForm::class) 19 |
20 | 21 | 22 | @endif 23 | 24 |
25 | @livewire(Laravel\Jetstream\Http\Livewire\LogoutOtherBrowserSessionsForm::class) 26 |
27 | 28 | @if (Laravel\Jetstream\Jetstream::hasAccountDeletionFeatures()) 29 | 30 | 31 |
32 | @livewire(Laravel\Jetstream\Http\Livewire\DeleteUserForm::class) 33 |
34 | @endif 35 |
36 | -------------------------------------------------------------------------------- /resources/views/filament/pages/edit-team.blade.php: -------------------------------------------------------------------------------- 1 | 2 | @livewire(Laravel\Jetstream\Http\Livewire\UpdateTeamNameForm::class, compact('team')) 3 | 4 | @livewire(Laravel\Jetstream\Http\Livewire\TeamMemberManager::class, compact('team')) 5 | 6 | @if (Gate::check('delete', $team) && ! $team->personal_team) 7 | 8 | 9 | @livewire(Laravel\Jetstream\Http\Livewire\DeleteTeamForm::class, compact('team')) 10 | @endif 11 | 12 | -------------------------------------------------------------------------------- /resources/views/layouts/guest.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('app.name', 'Laravel') }} 9 | 10 | 11 | 12 | 13 | 14 | 15 | @vite(['resources/css/app.css', 'resources/js/app.js']) 16 | 17 | 18 | @livewireStyles 19 | 20 | 21 |
22 | {{ $slot }} 23 |
24 | 25 | @livewireScripts 26 | 27 | 28 | -------------------------------------------------------------------------------- /resources/views/policy.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 | 6 |
7 | 8 |
9 | {!! $policy !!} 10 |
11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /resources/views/profile/set-password-form.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ __('Set Password') }} 4 | 5 | 6 | 7 | {{ __('Ensure your account is using a long, random password to stay secure.') }} 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 |
16 | 17 |
18 | 19 | 20 | 21 |
22 |
23 | 24 | 25 | 26 | {{ __('Password saved, please refresh.') }} 27 | 28 | 29 | 30 | {{ __('Save') }} 31 | 32 | 33 |
34 | -------------------------------------------------------------------------------- /resources/views/teams/create-team-form.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ __('Team Details') }} 4 | 5 | 6 | 7 | {{ __('Create a new team to collaborate with others on projects.') }} 8 | 9 | 10 | 11 |
12 | 13 | 14 |
15 | {{ $this->user->name }} 16 | 17 |
18 |
{{ $this->user->name }}
19 |
{{ $this->user->email }}
20 |
21 |
22 |
23 | 24 |
25 | 26 | 27 | 28 |
29 |
30 | 31 | 32 | 33 | {{ __('Create') }} 34 | 35 | 36 |
37 | -------------------------------------------------------------------------------- /resources/views/teams/create.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | {{ __('Create Team') }} 5 |

6 |
7 | 8 |
9 |
10 | @livewire('teams.create-team-form') 11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /resources/views/teams/show.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | {{ __('Team Settings') }} 5 |

6 |
7 | 8 |
9 |
10 | @livewire('teams.update-team-name-form', ['team' => $team]) 11 | 12 | @livewire('teams.team-member-manager', ['team' => $team]) 13 | 14 | @if (Gate::check('delete', $team) && ! $team->personal_team) 15 | 16 | 17 |
18 | @livewire('teams.delete-team-form', ['team' => $team]) 19 |
20 | @endif 21 |
22 |
23 |
24 | -------------------------------------------------------------------------------- /resources/views/terms.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 | 6 |
7 | 8 |
9 | {!! $terms !!} 10 |
11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | get('/user', function (Request $request) { 18 | return $request->user(); 19 | }); 20 | -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 18 | }); 19 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 19 | })->purpose('Display an inspiring quote'); 20 | -------------------------------------------------------------------------------- /routes/socialstream.php: -------------------------------------------------------------------------------- 1 | config('socialstream.middleware', ['web'])], function () { 7 | Route::get('/oauth/{provider}', [OAuthController::class, 'redirect'])->name('oauth.redirect'); 8 | Route::match(['get', 'post'], '/oauth/{provider}/callback', [OAuthController::class, 'callback'])->name('oauth.callback'); 9 | }); 10 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | view('welcome')); 19 | 20 | Route::redirect('/login', '/app/login')->name('login'); 21 | 22 | Route::redirect('/register', '/app/register')->name('register'); 23 | 24 | Route::redirect('/dashboard', '/app')->name('dashboard'); 25 | 26 | Route::get('/team-invitations/{invitation}', [TeamInvitationController::class, 'accept']) 27 | ->middleware(['signed', 'verified', 'auth', AuthenticateSession::class]) 28 | ->name('team-invitations.accept'); 29 | require __DIR__.'/socialstream.php';require __DIR__.'/socialstream.php';require __DIR__.'/socialstream.php'; -------------------------------------------------------------------------------- /rr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-control-panel/control-panel-laravel/9a25e557cbddb15c4bbe051663d650f0b336a78d/rr -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | import preset from './vendor/filament/filament/tailwind.config.preset'; 2 | 3 | export default { 4 | presets: [preset], 5 | content: [ 6 | './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php', 7 | './vendor/laravel/jetstream/**/*.blade.php', 8 | './storage/framework/views/*.php', 9 | './resources/views/**/*.blade.php', 10 | './app/Filament/**/*.php', 11 | './resources/views/filament/**/*.blade.php', 12 | './vendor/filament/**/*.blade.php', 13 | ], 14 | }; -------------------------------------------------------------------------------- /tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 18 | 19 | return $app; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Feature/AuthenticationTest.php: -------------------------------------------------------------------------------- 1 | get('/login'); 16 | 17 | $response->assertStatus(200); 18 | } 19 | 20 | public function test_users_can_authenticate_using_the_login_screen(): void 21 | { 22 | $user = User::factory()->create(); 23 | 24 | $response = $this->post('/login', [ 25 | 'email' => $user->email, 26 | 'password' => 'password', 27 | ]); 28 | 29 | $this->assertAuthenticated(); 30 | $response->assertRedirect(route('dashboard', absolute: false)); 31 | } 32 | 33 | public function test_users_can_not_authenticate_with_invalid_password(): void 34 | { 35 | $user = User::factory()->create(); 36 | 37 | $this->post('/login', [ 38 | 'email' => $user->email, 39 | 'password' => 'wrong-password', 40 | ]); 41 | 42 | $this->assertGuest(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Feature/BrowserSessionsTest.php: -------------------------------------------------------------------------------- 1 | actingAs(User::factory()->create()); 18 | 19 | Livewire::test(LogoutOtherBrowserSessionsForm::class) 20 | ->set('password', 'password') 21 | ->call('logoutOtherBrowserSessions') 22 | ->assertSuccessful(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Feature/CreateApiTokenTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped('API support is not enabled.'); 20 | } 21 | 22 | $this->actingAs($user = User::factory()->withPersonalTeam()->create()); 23 | 24 | Livewire::test(ApiTokenManager::class) 25 | ->set(['createApiTokenForm' => [ 26 | 'name' => 'Test Token', 27 | 'permissions' => [ 28 | 'read', 29 | 'update', 30 | ], 31 | ]]) 32 | ->call('createApiToken'); 33 | 34 | $this->assertCount(1, $user->fresh()->tokens); 35 | $this->assertEquals('Test Token', $user->fresh()->tokens->first()->name); 36 | $this->assertTrue($user->fresh()->tokens->first()->can('read')); 37 | $this->assertFalse($user->fresh()->tokens->first()->can('delete')); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Feature/CreateTeamTest.php: -------------------------------------------------------------------------------- 1 | actingAs($user = User::factory()->withPersonalTeam()->create()); 18 | 19 | Livewire::test(CreateTeamForm::class) 20 | ->set(['state' => ['name' => 'Test Team']]) 21 | ->call('createTeam'); 22 | 23 | $this->assertCount(2, $user->fresh()->ownedTeams); 24 | $this->assertEquals('Test Team', $user->fresh()->ownedTeams()->latest('id')->first()->name); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Feature/DeleteAccountTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped('Account deletion is not enabled.'); 20 | } 21 | 22 | $this->actingAs($user = User::factory()->create()); 23 | 24 | $component = Livewire::test(DeleteUserForm::class) 25 | ->set('password', 'password') 26 | ->call('deleteUser'); 27 | 28 | $this->assertNull($user->fresh()); 29 | } 30 | 31 | public function test_correct_password_must_be_provided_before_account_can_be_deleted(): void 32 | { 33 | if (! Features::hasAccountDeletionFeatures()) { 34 | $this->markTestSkipped('Account deletion is not enabled.'); 35 | } 36 | 37 | $this->actingAs($user = User::factory()->create()); 38 | 39 | Livewire::test(DeleteUserForm::class) 40 | ->set('password', 'wrong-password') 41 | ->call('deleteUser') 42 | ->assertHasErrors(['password']); 43 | 44 | $this->assertNotNull($user->fresh()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/Feature/DeleteApiTokenTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped('API support is not enabled.'); 21 | } 22 | 23 | $this->actingAs($user = User::factory()->withPersonalTeam()->create()); 24 | 25 | $token = $user->tokens()->create([ 26 | 'name' => 'Test Token', 27 | 'token' => Str::random(40), 28 | 'abilities' => ['create', 'read'], 29 | ]); 30 | 31 | Livewire::test(ApiTokenManager::class) 32 | ->set(['apiTokenIdBeingDeleted' => $token->id]) 33 | ->call('deleteApiToken'); 34 | 35 | $this->assertCount(0, $user->fresh()->tokens); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Feature/DeleteTeamTest.php: -------------------------------------------------------------------------------- 1 | actingAs($user = User::factory()->withPersonalTeam()->create()); 19 | 20 | $user->ownedTeams()->save($team = Team::factory()->make([ 21 | 'personal_team' => false, 22 | ])); 23 | 24 | $team->users()->attach( 25 | $otherUser = User::factory()->create(), ['role' => 'test-role'] 26 | ); 27 | 28 | Livewire::test(DeleteTeamForm::class, ['team' => $team->fresh()]) 29 | ->call('deleteTeam'); 30 | 31 | $this->assertNull($team->fresh()); 32 | $this->assertCount(0, $otherUser->fresh()->teams); 33 | } 34 | 35 | public function test_personal_teams_cant_be_deleted(): void 36 | { 37 | $this->actingAs($user = User::factory()->withPersonalTeam()->create()); 38 | 39 | Livewire::test(DeleteTeamForm::class, ['team' => $user->currentTeam]) 40 | ->call('deleteTeam') 41 | ->assertHasErrors(['team']); 42 | 43 | $this->assertNotNull($user->currentTeam->fresh()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/Feature/LeaveTeamTest.php: -------------------------------------------------------------------------------- 1 | withPersonalTeam()->create(); 18 | 19 | $user->currentTeam->users()->attach( 20 | $otherUser = User::factory()->create(), ['role' => 'admin'] 21 | ); 22 | 23 | $this->actingAs($otherUser); 24 | 25 | Livewire::test(TeamMemberManager::class, ['team' => $user->currentTeam]) 26 | ->call('leaveTeam'); 27 | 28 | $this->assertCount(0, $user->currentTeam->fresh()->users); 29 | } 30 | 31 | public function test_team_owners_cant_leave_their_own_team(): void 32 | { 33 | $this->actingAs($user = User::factory()->withPersonalTeam()->create()); 34 | 35 | Livewire::test(TeamMemberManager::class, ['team' => $user->currentTeam]) 36 | ->call('leaveTeam') 37 | ->assertHasErrors(['team']); 38 | 39 | $this->assertNotNull($user->currentTeam->fresh()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Feature/PasswordConfirmationTest.php: -------------------------------------------------------------------------------- 1 | withPersonalTeam()->create(); 16 | 17 | $response = $this->actingAs($user)->get('/user/confirm-password'); 18 | 19 | $response->assertStatus(200); 20 | } 21 | 22 | public function test_password_can_be_confirmed(): void 23 | { 24 | $user = User::factory()->create(); 25 | 26 | $response = $this->actingAs($user)->post('/user/confirm-password', [ 27 | 'password' => 'password', 28 | ]); 29 | 30 | $response->assertRedirect(); 31 | $response->assertSessionHasNoErrors(); 32 | } 33 | 34 | public function test_password_is_not_confirmed_with_invalid_password(): void 35 | { 36 | $user = User::factory()->create(); 37 | 38 | $response = $this->actingAs($user)->post('/user/confirm-password', [ 39 | 'password' => 'wrong-password', 40 | ]); 41 | 42 | $response->assertSessionHasErrors(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Feature/ProfileInformationTest.php: -------------------------------------------------------------------------------- 1 | actingAs($user = User::factory()->create()); 18 | 19 | $component = Livewire::test(UpdateProfileInformationForm::class); 20 | 21 | $this->assertEquals($user->name, $component->state['name']); 22 | $this->assertEquals($user->email, $component->state['email']); 23 | } 24 | 25 | public function test_profile_information_can_be_updated(): void 26 | { 27 | $this->actingAs($user = User::factory()->create()); 28 | 29 | Livewire::test(UpdateProfileInformationForm::class) 30 | ->set('state', ['name' => 'Test Name', 'email' => 'test@example.com']) 31 | ->call('updateProfileInformation'); 32 | 33 | $this->assertEquals('Test Name', $user->fresh()->name); 34 | $this->assertEquals('test@example.com', $user->fresh()->email); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Feature/UpdateTeamNameTest.php: -------------------------------------------------------------------------------- 1 | actingAs($user = User::factory()->withPersonalTeam()->create()); 18 | 19 | Livewire::test(UpdateTeamNameForm::class, ['team' => $user->currentTeam]) 20 | ->set(['state' => ['name' => 'Test Team']]) 21 | ->call('updateTeamName'); 22 | 23 | $this->assertCount(1, $user->fresh()->ownedTeams); 24 | $this->assertEquals('Test Team', $user->currentTeam->fresh()->name); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import laravel, { refreshPaths } from 'laravel-vite-plugin' 3 | import { viteStaticCopy } from "vite-plugin-static-copy"; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | laravel({ 8 | input: ['resources/css/app.css', 'resources/js/app.js', 9 | 'resources/css/filament/admin/theme.css'], 10 | refresh: [ 11 | ...refreshPaths, 12 | 'app/Filament/**', 13 | 'app/Forms/Components/**', 14 | 'app/Livewire/**', 15 | 'app/Infolists/Components/**', 16 | 'app/Providers/Filament/**', 17 | 'app/Tables/Columns/**', 18 | ], 19 | }), 20 | viteStaticCopy({ 21 | targets: [ 22 | { 23 | src: "resources/images/*", 24 | dest: "images", 25 | }, 26 | ], 27 | }), 28 | ], 29 | }) 30 | --------------------------------------------------------------------------------