├── public ├── favicon.ico ├── robots.txt ├── storage ├── build │ ├── images │ │ └── logo.png │ └── manifest.json ├── fonts │ └── filament │ │ └── filament │ │ └── inter │ │ ├── inter-greek-wght-normal-AXVTPQD5.woff2 │ │ ├── inter-greek-wght-normal-IRE366VL.woff2 │ │ ├── inter-greek-wght-normal-N43DBLU2.woff2 │ │ ├── inter-latin-wght-normal-NRMW37G5.woff2 │ │ ├── inter-latin-wght-normal-O25CN4JL.woff2 │ │ ├── inter-latin-wght-normal-OPIJAQLS.woff2 │ │ ├── inter-cyrillic-wght-normal-EWLSKVKN.woff2 │ │ ├── inter-cyrillic-wght-normal-JEOLYBOO.woff2 │ │ ├── inter-cyrillic-wght-normal-R5CMSONN.woff2 │ │ ├── inter-greek-ext-wght-normal-7GGTF7EK.woff2 │ │ ├── inter-greek-ext-wght-normal-EOVOK2B5.woff2 │ │ ├── inter-greek-ext-wght-normal-ZEVLMORV.woff2 │ │ ├── inter-latin-ext-wght-normal-5SRY4DMZ.woff2 │ │ ├── inter-latin-ext-wght-normal-GZCIV3NH.woff2 │ │ ├── inter-latin-ext-wght-normal-HA22NDSG.woff2 │ │ ├── inter-vietnamese-wght-normal-CE5GGD3W.woff2 │ │ ├── inter-vietnamese-wght-normal-TWG5UU7E.woff2 │ │ ├── inter-cyrillic-ext-wght-normal-ASVAGXXE.woff2 │ │ ├── inter-cyrillic-ext-wght-normal-IYF56FF6.woff2 │ │ └── inter-cyrillic-ext-wght-normal-XKHXBTUO.woff2 ├── js │ └── filament │ │ ├── forms │ │ └── components │ │ │ ├── textarea.js │ │ │ ├── tags-input.js │ │ │ ├── key-value.js │ │ │ └── checkbox-list.js │ │ ├── schemas │ │ └── components │ │ │ ├── tabs.js │ │ │ ├── actions.js │ │ │ └── wizard.js │ │ ├── tables │ │ └── components │ │ │ └── columns │ │ │ ├── checkbox.js │ │ │ ├── toggle.js │ │ │ └── text-input.js │ │ └── actions │ │ └── actions.js └── .htaccess ├── app ├── Filament │ ├── App │ │ ├── Pages │ │ │ ├── .gitignore │ │ │ ├── PersonalAccessTokensPage.php │ │ │ ├── CreateTeam.php │ │ │ ├── EditProfile.php │ │ │ └── EditTeam.php │ │ ├── Resources │ │ │ └── .gitignore │ │ └── Widgets │ │ │ ├── .gitignore │ │ │ └── SocialLinksWidget.php │ ├── Admin │ │ └── Resources │ │ │ ├── MenuResource │ │ │ └── Pages │ │ │ │ ├── EditMenu.php │ │ │ │ ├── CreateMenu.php │ │ │ │ └── ListMenus.php │ │ │ ├── SiteSettingsResource │ │ │ └── Pages │ │ │ │ ├── CreateSiteSettings.php │ │ │ │ ├── EditSiteSettings.php │ │ │ │ └── ListSiteSettings.php │ │ │ └── ModuleResource │ │ │ └── Pages │ │ │ ├── ListModules.php │ │ │ └── ViewModule.php │ └── Resources │ │ └── WebHostingAccounts │ │ └── Pages │ │ ├── CreateWebHostingAccount.php │ │ ├── ListWebHostingAccounts.php │ │ └── EditWebHostingAccount.php ├── Http │ ├── Controllers │ │ ├── Controller.php │ │ ├── ForgotPasswordController.php │ │ ├── LoginController.php │ │ ├── ResetPasswordController.php │ │ └── TeamInvitationController.php │ ├── Middleware │ │ ├── EncryptCookies.php │ │ ├── VerifyCsrfToken.php │ │ ├── PreventRequestsDuringMaintenance.php │ │ ├── TrimStrings.php │ │ ├── TrustHosts.php │ │ ├── Authenticate.php │ │ ├── ValidateSignature.php │ │ ├── TrustProxies.php │ │ ├── AssignDefaultTeam.php │ │ ├── ScreeningDataEncryptor.php │ │ └── TeamsPermission.php │ └── Livewire │ │ └── CreateTeam.php ├── Actions │ ├── Jetstream │ │ ├── DeleteTeam.php │ │ ├── DeleteUser.php │ │ ├── UpdateTeamName.php │ │ ├── CreateTeam.php │ │ ├── DeleteUserWithTeams.php │ │ └── RemoveTeamMember.php │ ├── Fortify │ │ ├── PasswordValidationRules.php │ │ ├── ResetUserPassword.php │ │ └── CreateNewUserWithTeams.php │ └── Socialstream │ │ ├── HandleInvalidState.php │ │ ├── GenerateRedirectForProvider.php │ │ ├── ResolveSocialiteUser.php │ │ ├── SetUserPassword.php │ │ ├── CreateConnectedAccount.php │ │ └── CreateUserFromProvider.php ├── Listeners │ ├── SwitchTeam.php │ ├── CreatePersonalTeam.php │ └── EmailTracker.php ├── Providers │ ├── BroadcastServiceProvider.php │ ├── AuthServiceProvider.php │ ├── AppServiceProvider.php │ ├── EventServiceProvider.php │ ├── RouteServiceProvider.php │ ├── SocialstreamServiceProvider.php │ ├── FortifyServiceProvider.php │ └── TeamServiceProvider.php ├── Models │ ├── SiteSettings.php │ ├── Menu.php │ ├── TeamInvitation.php │ └── ConnectedAccount.php ├── Services │ ├── SiteSettingsService.php │ ├── PleskApiClient.php │ └── MenuService.php ├── Console │ └── Kernel.php ├── Settings │ └── GeneralSettings.php ├── Exceptions │ └── Handler.php ├── Modules │ └── Contracts │ │ └── ModuleInterface.php └── Policies │ └── ConnectedAccountPolicy.php ├── database ├── .gitignore ├── seeders │ ├── PermissionsSeeder.php │ ├── TeamSeeder.php │ ├── DatabaseSeeder.php │ ├── RolesSeeder.php │ ├── UserSeeder.php │ └── SiteSettingsSeeder.php ├── factories │ ├── MenuFactory.php │ └── ConnectedAccountFactory.php └── migrations │ ├── 0001_01_01_000001_make_password_nullable_on_users_table.php │ ├── 2020_05_21_100000_create_teams_table.php │ ├── 2024_02_24_000000_create_reminder_settings_table.php │ ├── 2020_05_21_200000_create_team_user_table.php │ ├── 2024_07_24_080000_create_menus_table.php │ ├── 2019_08_19_000000_create_failed_jobs_table.php │ ├── 2020_05_21_300000_create_team_invitations_table.php │ ├── 2023_05_15_000000_create_site_settings_table.php │ ├── 2024_01_01_000001_create_settings_table.php │ ├── 0001_01_01_000002_create_connected_accounts_table.php │ └── 2014_10_12_200000_add_two_factor_columns_to_users_table.php ├── .github ├── FUNDING.yml ├── dependabot.yml ├── issue_template.md └── workflows │ ├── install.yml │ ├── tests.yml │ └── main.yml ├── bootstrap └── cache │ └── .gitignore ├── resources ├── css │ ├── app.css │ └── filament │ │ └── admin │ │ ├── theme.css │ │ └── tailwind.config.js ├── js │ └── app.js ├── views │ ├── filament │ │ ├── widgets │ │ │ ├── report-widget.blade.php │ │ │ ├── daboville-report.blade.php │ │ │ └── descendant-chart.blade.php │ │ ├── pages │ │ │ ├── api-tokens.blade.php │ │ │ ├── create-team.blade.php │ │ │ ├── edit-team.blade.php │ │ │ └── edit-profile.blade.php │ │ └── filament │ │ │ └── pages │ │ │ ├── api-tokens.blade.php │ │ │ ├── create-team.blade.php │ │ │ ├── edit-team.blade.php │ │ │ └── edit-profile.blade.php │ ├── components │ │ ├── logo.blade.php │ │ ├── checkbox.blade.php │ │ ├── input-error.blade.php │ │ ├── section-border.blade.php │ │ ├── label.blade.php │ │ ├── input.blade.php │ │ ├── dropdown-link.blade.php │ │ ├── authentication-card.blade.php │ │ ├── section-title.blade.php │ │ ├── action-link.blade.php │ │ ├── danger-button.blade.php │ │ ├── secondary-button.blade.php │ │ ├── validation-errors.blade.php │ │ ├── button.blade.php │ │ ├── application-mark.blade.php │ │ ├── action-section.blade.php │ │ ├── socialstream-icons │ │ │ ├── linkedin.blade.php │ │ │ ├── facebook.blade.php │ │ │ ├── gitlab.blade.php │ │ │ ├── twitter.blade.php │ │ │ ├── google.blade.php │ │ │ ├── github.blade.php │ │ │ ├── bitbucket.blade.php │ │ │ ├── slack.blade.php │ │ │ └── provider-icon.blade.php │ │ ├── action-message.blade.php │ │ ├── authentication-card-logo.blade.php │ │ ├── dialog-modal.blade.php │ │ ├── buttons.blade.php │ │ ├── nav-link.blade.php │ │ ├── why_us_section.blade.php │ │ ├── responsive-nav-link.blade.php │ │ ├── products_section.blade.php │ │ ├── layouts │ │ │ └── app.blade.php │ │ ├── switchable-team.blade.php │ │ ├── form-section.blade.php │ │ ├── socialstream.blade.php │ │ ├── connected-account.blade.php │ │ ├── home-header.blade.php │ │ ├── confirmation-modal.blade.php │ │ ├── contact-form.blade.php │ │ ├── manage_section.blade.php │ │ └── dropdown.blade.php │ ├── api │ │ └── index.blade.php │ ├── teams │ │ ├── create.blade.php │ │ ├── show.blade.php │ │ ├── create-team-form.blade.php │ │ └── delete-team-form.blade.php │ ├── auth │ │ ├── confirm-password.blade.php │ │ ├── forgot-password.blade.php │ │ └── reset-password.blade.php │ └── profile │ │ └── set-password-form.blade.php └── images │ └── logo.png ├── storage ├── logs │ └── .gitignore ├── app │ ├── public │ │ └── .gitignore │ └── .gitignore └── framework │ ├── testing │ └── .gitignore │ ├── views │ └── .gitignore │ ├── cache │ ├── data │ │ └── .gitignore │ └── .gitignore │ ├── sessions │ └── .gitignore │ └── .gitignore ├── .docker ├── config │ ├── php.ini │ ├── config │ │ ├── php.ini │ │ ├── supervisord.conf │ │ └── conf.d │ │ │ └── default.conf │ ├── supervisord.conf │ └── conf.d │ │ └── default.conf ├── octane │ ├── php.ini │ ├── opcache.ini │ ├── supervisord.horizon.conf │ ├── .rr.prod.yaml │ ├── RoadRunner │ │ ├── .rr.prod.yaml │ │ └── supervisord.roadrunner.conf │ ├── utilities.sh │ ├── supervisord.app.conf │ ├── supervisord.app.roadrunner.conf │ ├── entrypoint.sh │ ├── FrankenPHP │ │ └── supervisord.frankenphp.conf │ └── Swoole │ │ └── supervisord.swoole.conf ├── utilities.sh ├── supervisord.conf ├── supervisord.worker.conf ├── supervisord.horizon.conf ├── php.ini └── supervisord.scheduler.conf ├── postcss.config.cjs ├── tests ├── TestCase.php ├── Unit │ └── ExampleTest.php ├── CreatesApplication.php └── Feature │ └── ExampleTest.php ├── config ├── socialstream.php ├── cors.php ├── view.php └── services.php ├── tailwind.config.js ├── .gitignore ├── routes ├── socialstream.php ├── channels.php ├── web.php ├── api.php └── console.php ├── package.json ├── vite.config.js ├── phpunit.xml ├── rector.php ├── .env.testing └── .env.example /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Filament/App/Pages/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /app/Filament/App/Resources/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Filament/App/Widgets/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: liberusoftware 2 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | import 'preline/dist/preline.js'; 2 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /resources/views/filament/widgets/report-widget.blade.php: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /.docker/config/php.ini: -------------------------------------------------------------------------------- 1 | [Date] 2 | date.timezone="UTC" 3 | expose_php= Off -------------------------------------------------------------------------------- /public/storage: -------------------------------------------------------------------------------- 1 | /home/genealogia/work/automation-laravel/storage/app/public -------------------------------------------------------------------------------- /.docker/config/config/php.ini: -------------------------------------------------------------------------------- 1 | [Date] 2 | date.timezone="UTC" 3 | expose_php= Off -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('@tailwindcss/postcss'), 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /resources/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/resources/images/logo.png -------------------------------------------------------------------------------- /public/build/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/build/images/logo.png -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /resources/views/components/logo.blade.php: -------------------------------------------------------------------------------- 1 | {{ config('app.name') }} 2 | -------------------------------------------------------------------------------- /resources/views/components/checkbox.blade.php: -------------------------------------------------------------------------------- 1 | merge(['class' => 'rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500']) !!}> 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/section-border.blade.php: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /resources/views/components/label.blade.php: -------------------------------------------------------------------------------- 1 | @props(['value']) 2 | 3 | 6 | -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-greek-wght-normal-AXVTPQD5.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-greek-wght-normal-AXVTPQD5.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-greek-wght-normal-IRE366VL.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-greek-wght-normal-IRE366VL.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-greek-wght-normal-N43DBLU2.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-greek-wght-normal-N43DBLU2.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-latin-wght-normal-NRMW37G5.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-latin-wght-normal-NRMW37G5.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-latin-wght-normal-O25CN4JL.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-latin-wght-normal-O25CN4JL.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-latin-wght-normal-OPIJAQLS.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-latin-wght-normal-OPIJAQLS.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-EWLSKVKN.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-EWLSKVKN.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-JEOLYBOO.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-JEOLYBOO.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-R5CMSONN.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-R5CMSONN.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-7GGTF7EK.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-7GGTF7EK.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-EOVOK2B5.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-EOVOK2B5.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-ZEVLMORV.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-ZEVLMORV.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-5SRY4DMZ.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-5SRY4DMZ.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-GZCIV3NH.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-GZCIV3NH.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-HA22NDSG.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-HA22NDSG.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-vietnamese-wght-normal-CE5GGD3W.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-vietnamese-wght-normal-CE5GGD3W.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-vietnamese-wght-normal-TWG5UU7E.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liberu-automation/automation-laravel/HEAD/public/fonts/filament/filament/inter/inter-vietnamese-wght-normal-TWG5UU7E.woff2 -------------------------------------------------------------------------------- /resources/views/filament/pages/api-tokens.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | @livewire(\Laravel\Jetstream\Http\Livewire\ApiTokenManager::class) 4 | 5 | 6 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | @livewire(\Laravel\Jetstream\Http\Livewire\ApiTokenManager::class) 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/views/filament/pages/create-team.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | @livewire(\Laravel\Jetstream\Http\Livewire\CreateTeamForm::class) 4 |
5 | 6 |
7 | -------------------------------------------------------------------------------- /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/filament/filament/pages/create-team.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | @livewire(\Laravel\Jetstream\Http\Livewire\CreateTeamForm::class) 4 |
5 | 6 |
7 | -------------------------------------------------------------------------------- /resources/css/filament/admin/theme.css: -------------------------------------------------------------------------------- 1 | @import '/vendor/filament/filament/resources/css/theme.css'; 2 | 3 | 4 | /* .fi-body { 5 | background-color: #ebebeb !important; 6 | } 7 | 8 | .fi-sidebar { 9 | background-color: #292828 !important; 10 | } */ 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /config/socialstream.php: -------------------------------------------------------------------------------- 1 | ['web'], 7 | 'prompt' => 'Or Login Via', 8 | 'providers' => [ 9 | // Providers::github(), 10 | ], 11 | 'component' => 'socialstream::components.socialstream', 12 | ]; 13 | -------------------------------------------------------------------------------- /tests/Unit/ExampleTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/Filament/Admin/Resources/MenuResource/Pages/EditMenu.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | {{ $logo }} 4 |
5 | 6 |
7 | {{ $slot }} 8 |
9 | 10 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | purge(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /database/seeders/PermissionsSeeder.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/Http/Middleware/VerifyCsrfToken.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | import preset from './vendor/filament/support/tailwind.config.preset' 2 | 3 | export default { 4 | presets: [preset], 5 | content: [ 6 | './app/Filament/**/*.php', 7 | './resources/views/**/*.blade.php', 8 | './vendor/filament/**/*.blade.php', 9 | 'node_modules/preline/dist/*.js', 10 | ], 11 | plugins: [ 12 | require('preline/plugin'), 13 | ], 14 | } 15 | -------------------------------------------------------------------------------- /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/danger-button.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /resources/views/components/secondary-button.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /resources/views/components/validation-errors.blade.php: -------------------------------------------------------------------------------- 1 | @if ($errors->any()) 2 |
3 |
{{ __('Whoops! Something went wrong.') }}
4 | 5 | 10 |
11 | @endif 12 | -------------------------------------------------------------------------------- /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/views/components/button.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | 3 | node_modules/ 4 | npm-debug.log 5 | yarn-error.log 6 | 7 | # Laravel 4 specific 8 | bootstrap/compiled.php 9 | app/storage/ 10 | 11 | # Laravel 5 & Lumen specific 12 | public/storage 13 | public/hot 14 | 15 | # Laravel 5 & Lumen specific with changed public path 16 | public_html/storage 17 | public_html/hot 18 | 19 | storage/*.key 20 | .env 21 | Homestead.yaml 22 | Homestead.json 23 | /.vagrant 24 | .phpunit.result.cache 25 | rr 26 | .rr.yaml 27 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/Http/Middleware/PreventRequestsDuringMaintenance.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrimStrings.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | 'current_password', 16 | 'password', 17 | 'password_confirmation', 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 18 | 19 | return $app; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustHosts.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | public function hosts(): array 15 | { 16 | return [ 17 | $this->allSubdomainsOfApplicationUrl(), 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | expectsJson() ? null : route('login'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /database/factories/MenuFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->word, 16 | 'url' => $this->faker->url, 17 | 'order' => $this->faker->numberBetween(1, 10), 18 | ]; 19 | } 20 | } -------------------------------------------------------------------------------- /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/components/socialstream-icons/linkedin.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /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/authentication-card-logo.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /app/Actions/Fortify/PasswordValidationRules.php: -------------------------------------------------------------------------------- 1 | |string> 14 | */ 15 | protected function passwordRules(): array 16 | { 17 | return ['required', 'string', Password::default(), 'confirmed']; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /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/Socialstream/HandleInvalidState.php: -------------------------------------------------------------------------------- 1 | 1, 18 | 'name' => 'default', 19 | 'personal_team' => false, 20 | 'user_id' => 1, 21 | ]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /app/Filament/Resources/WebHostingAccounts/Pages/CreateWebHostingAccount.php: -------------------------------------------------------------------------------- 1 | getResource()::getUrl('index'); 16 | } 17 | } -------------------------------------------------------------------------------- /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/Listeners/CreatePersonalTeam.php: -------------------------------------------------------------------------------- 1 | teamManagementService = $teamManagementService; 15 | } 16 | 17 | public function handle(Registered $event): void 18 | { 19 | $this->teamManagementService->assignUserToDefaultTeam($event->user); 20 | } 21 | } -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /app/Actions/Socialstream/GenerateRedirectForProvider.php: -------------------------------------------------------------------------------- 1 | redirect(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Models/SiteSettings.php: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | @endforeach 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton(ModuleManager::class, function ($app) { 18 | return new ModuleManager(); 19 | }); 20 | 21 | // Register the module service provider 22 | $this->app->register(ModuleServiceProvider::class); 23 | } 24 | 25 | /** 26 | * Bootstrap any application services. 27 | */ 28 | public function boot(): void 29 | { 30 | // 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /public/js/filament/tables/components/columns/checkbox.js: -------------------------------------------------------------------------------- 1 | function o({name:i,recordKey:s,state:a}){return{error:void 0,isLoading:!1,state:a,init(){Livewire.hook("commit",({component:e,commit:r,succeed:n,fail:h,respond:u})=>{n(({snapshot:f,effect:d})=>{this.$nextTick(()=>{if(this.isLoading||e.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let t=this.getServerState();t===void 0||Alpine.raw(this.state)===t||(this.state=t)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||Alpine.raw(this.state)===e)return;this.isLoading=!0;let r=await this.$wire.updateTableColumnState(i,s,this.state);this.error=r?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.state?"1":"0"),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[1,"1"].includes(this.$refs.serverState.value)}}}export{o as default}; 2 | -------------------------------------------------------------------------------- /public/js/filament/tables/components/columns/toggle.js: -------------------------------------------------------------------------------- 1 | function o({name:i,recordKey:s,state:a}){return{error:void 0,isLoading:!1,state:a,init(){Livewire.hook("commit",({component:e,commit:r,succeed:n,fail:h,respond:u})=>{n(({snapshot:f,effect:d})=>{this.$nextTick(()=>{if(this.isLoading||e.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let t=this.getServerState();t===void 0||Alpine.raw(this.state)===t||(this.state=t)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||Alpine.raw(this.state)===e)return;this.isLoading=!0;let r=await this.$wire.updateTableColumnState(i,s,this.state);this.error=r?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.state?"1":"0"),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[1,"1"].includes(this.$refs.serverState.value)}}}export{o as default}; 2 | -------------------------------------------------------------------------------- /database/migrations/2024_02_24_000000_create_reminder_settings_table.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | id(); 15 | $table->integer('days_before_reminder')->default(3); 16 | $table->integer('reminder_frequency_days')->default(7); 17 | $table->integer('max_reminders')->default(3); 18 | $table->boolean('is_active')->default(true); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | public function down(): void 24 | { 25 | Schema::dropIfExists('reminder_settings'); 26 | } 27 | }; -------------------------------------------------------------------------------- /public/js/filament/schemas/components/actions.js: -------------------------------------------------------------------------------- 1 | var i=()=>({isSticky:!1,width:0,resizeObserver:null,boundUpdateWidth:null,init(){let e=this.$el.parentElement;e&&(this.updateWidth(),this.resizeObserver=new ResizeObserver(()=>this.updateWidth()),this.resizeObserver.observe(e),this.boundUpdateWidth=this.updateWidth.bind(this),window.addEventListener("resize",this.boundUpdateWidth))},enableSticky(){this.isSticky=this.$el.getBoundingClientRect().top>0},disableSticky(){this.isSticky=!1},updateWidth(){let e=this.$el.parentElement;if(!e)return;let t=getComputedStyle(this.$root.querySelector(".fi-ac"));this.width=e.offsetWidth+parseInt(t.marginInlineStart,10)*-1+parseInt(t.marginInlineEnd,10)*-1},destroy(){this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.boundUpdateWidth&&(window.removeEventListener("resize",this.boundUpdateWidth),this.boundUpdateWidth=null)}});export{i as default}; 2 | -------------------------------------------------------------------------------- /resources/views/components/socialstream-icons/github.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | }; -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | @livewire('notifications') 26 | 27 | @filamentScripts 28 | @vite('resources/js/app.js') 29 | 30 | 31 | -------------------------------------------------------------------------------- /resources/views/components/socialstream-icons/bitbucket.blade.php: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /app/Http/Controllers/ForgotPasswordController.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/Filament/Admin/Resources/ModuleResource/Pages/ListModules.php: -------------------------------------------------------------------------------- 1 | label('Refresh Modules') 19 | ->icon('heroicon-o-arrow-path') 20 | ->action(function () { 21 | // Clear module cache and reload 22 | cache()->forget('app.modules'); 23 | $this->redirect(request()->header('Referer')); 24 | }), 25 | ]; 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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /config/cors.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /database/seeders/UserSeeder.php: -------------------------------------------------------------------------------- 1 | 'Admin User', 20 | 'email' => 'admin@example.com', 21 | 'password' => Hash::make('password'), 22 | 'email_verified_at' => now(), 23 | ]); 24 | $adminUser->assignRole('admin'); 25 | // $this->createTeamForUser($adminUser); 26 | } 27 | 28 | private function createTeamForUser($user) 29 | { 30 | $team = Team::first(); 31 | $team->users()->attach($user); 32 | $user->current_team_id = 1; 33 | $user->save(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /public/js/filament/forms/components/key-value.js: -------------------------------------------------------------------------------- 1 | function h({state:r}){return{state:r,rows:[],init(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(e,t)=>{let s=i=>i===null?0:Array.isArray(i)?i.length:typeof i!="object"?0:Object.keys(i).length;s(e)===0&&s(t)===0||this.updateRows()})},addRow(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow(e){this.rows.splice(e,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows(e){let t=Alpine.raw(this.rows);this.rows=[];let s=t.splice(e.oldIndex,1)[0];t.splice(e.newIndex,0,s),this.$nextTick(()=>{this.rows=t,this.updateState()})},updateRows(){let t=Alpine.raw(this.state).map(({key:s,value:i})=>({key:s,value:i}));this.rows.forEach(s=>{(s.key===""||s.key===null)&&t.push({key:"",value:s.value})}),this.rows=t},updateState(){let e=[];this.rows.forEach(t=>{t.key===""||t.key===null||e.push({key:t.key,value:t.value})}),JSON.stringify(this.state)!==JSON.stringify(e)&&(this.state=e)}}}export{h as default}; 2 | -------------------------------------------------------------------------------- /database/migrations/2023_05_15_000000_create_site_settings_table.php: -------------------------------------------------------------------------------- 1 | id(); 17 | $table->string('name')->nullable(); 18 | $table->string('group')->nullable(); 19 | $table->string('payload')->nullable(); 20 | $table->integer('locked')->nullable(); 21 | $table->timestamps(); 22 | }); 23 | } 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | */ 29 | public function down(): void 30 | { 31 | Schema::dropIfExists('settings'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /public/js/filament/actions/actions.js: -------------------------------------------------------------------------------- 1 | (()=>{var n=({livewireId:e})=>({actionNestingIndex:null,init(){window.addEventListener("sync-action-modals",t=>{t.detail.id===e&&this.syncActionModals(t.detail.newActionNestingIndex)})},syncActionModals(t){if(this.actionNestingIndex===t){this.actionNestingIndex!==null&&this.$nextTick(()=>this.openModal());return}if(this.actionNestingIndex!==null&&this.closeModal(),this.actionNestingIndex=t,this.actionNestingIndex!==null){if(!this.$el.querySelector(`#${this.generateModalId(t)}`)){this.$nextTick(()=>this.openModal());return}this.openModal()}},generateModalId(t){return`fi-${e}-action-`+t},openModal(){let t=this.generateModalId(this.actionNestingIndex);document.dispatchEvent(new CustomEvent("open-modal",{bubbles:!0,composed:!0,detail:{id:t}}))},closeModal(){let t=this.generateModalId(this.actionNestingIndex);document.dispatchEvent(new CustomEvent("close-modal-quietly",{bubbles:!0,composed:!0,detail:{id:t}}))}});document.addEventListener("alpine:init",()=>{window.Alpine.data("filamentActionModals",n)});})(); 2 | -------------------------------------------------------------------------------- /public/js/filament/tables/components/columns/text-input.js: -------------------------------------------------------------------------------- 1 | function o({name:i,recordKey:s,state:a}){return{error:void 0,isLoading:!1,state:a,init(){Livewire.hook("commit",({component:e,commit:r,succeed:n,fail:d,respond:u})=>{n(({snapshot:f,effect:h})=>{this.$nextTick(()=>{if(this.isLoading||e.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let t=this.getServerState();t===void 0||this.getNormalizedState()===t||(this.state=t)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||this.getNormalizedState()===e)return;this.isLoading=!0;let r=await this.$wire.updateTableColumnState(i,s,this.state);this.error=r?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.getNormalizedState()),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[null,void 0].includes(this.$refs.serverState.value)?"":this.$refs.serverState.value.replaceAll('\\"','"')},getNormalizedState(){let e=Alpine.raw(this.state);return[null,void 0].includes(e)?"":e}}}export{o as default}; 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 | -------------------------------------------------------------------------------- /tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 16 | $response->assertStatus(200); 17 | } 18 | 19 | /** 20 | * Test the "/app" route returns a successful response. 21 | */ 22 | public function test_the_app_route_returns_a_successful_response(): void 23 | { 24 | $response = $this->get('/app'); 25 | $response->assertStatus(200); 26 | } 27 | 28 | /** 29 | * Test the "/admin" route returns a successful response. 30 | */ 31 | public function test_the_admin_route_returns_a_successful_response(): void 32 | { 33 | $response = $this->get('/admin'); 34 | $response->assertStatus(200); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | > 16 | */ 17 | protected $listen = [ 18 | Registered::class => [ 19 | SendEmailVerificationNotification::class, 20 | ], 21 | ]; 22 | 23 | /** 24 | * Register any events for your application. 25 | */ 26 | public function boot(): void 27 | { 28 | // 29 | } 30 | 31 | /** 32 | * Determine if events and listeners should be automatically discovered. 33 | */ 34 | public function shouldDiscoverEvents(): bool 35 | { 36 | return false; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /database/seeders/SiteSettingsSeeder.php: -------------------------------------------------------------------------------- 1 | site_name = config('app.name', 'Liberu Genealogy'); 15 | $settings->site_email = 'info@liberugenealogy.com'; 16 | $settings->site_phone = '+44 208 050 5865'; 17 | $settings->site_address = '123 Genealogy St, London, UK'; 18 | $settings->site_country = 'United Kingdom'; 19 | $settings->site_currency = '£'; 20 | $settings->site_default_language = 'en'; 21 | $settings->facebook_url = 'https://www.facebook.com/familytree365'; 22 | $settings->twitter_url = null; 23 | $settings->github_url = 'https://www.github.com/liberu-genealogy'; 24 | $settings->youtube_url = null; 25 | $settings->footer_copyright = '© ' . date('Y') . ' ' . config('app.name', 'Liberu Genealogy') . '. All rights reserved.'; 26 | 27 | $settings->save(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /database/migrations/2024_01_01_000001_create_settings_table.php: -------------------------------------------------------------------------------- 1 | migrator->add('general.site_name', config('app.name', 'Liberu Genealogy')); 10 | $this->migrator->add('general.site_email', 'info@example.com'); 11 | $this->migrator->add('general.site_phone', ''); 12 | $this->migrator->add('general.site_address', ''); 13 | $this->migrator->add('general.site_country', ''); 14 | $this->migrator->add('general.site_currency', '$'); 15 | $this->migrator->add('general.site_default_language', 'en'); 16 | $this->migrator->add('general.facebook_url', null); 17 | $this->migrator->add('general.twitter_url', null); 18 | $this->migrator->add('general.github_url', 'https://www.github.com/liberu-genealogy'); 19 | $this->migrator->add('general.youtube_url', null); 20 | $this->migrator->add('general.footer_copyright', '© ' . date('Y') . ' ' . config('app.name', 'Liberu Genealogy') . '. All rights reserved.'); 21 | } 22 | }; -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/Modules/Contracts/ModuleInterface.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 | -------------------------------------------------------------------------------- /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/connected-account.blade.php: -------------------------------------------------------------------------------- 1 | @props(['provider', 'createdAt' => null]) 2 | 3 |
4 |
5 |
6 | 7 | 8 |
9 |
10 | {{ __($provider['name']) }} 11 |
12 | 13 | @if (! empty($createdAt)) 14 |
15 | {{ __('Connected :createdAt', ['createdAt' => $createdAt]) }} 16 |
17 | @else 18 |
19 | {{ __('Not connected.') }} 20 |
21 | @endif 22 |
23 |
24 | 25 |
26 | {{ $action }} 27 |
28 |
29 | 30 | @error($provider['id'].'_connect_error') 31 |
32 | {{ $message }} 33 |
34 | @enderror 35 |
36 | -------------------------------------------------------------------------------- /resources/views/components/home-header.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 25 |
26 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /rector.php: -------------------------------------------------------------------------------- 1 | paths([ 15 | __DIR__.'/app', 16 | __DIR__.'/config', 17 | __DIR__.'/database', 18 | __DIR__.'/resources', 19 | __DIR__.'/routes', 20 | __DIR__.'/tests', 21 | ]); 22 | 23 | // Skip specific rules 24 | $rectorConfig->skip([ 25 | CompactToVariablesRector::class, 26 | ]); 27 | 28 | // Enable caching for Rector 29 | $rectorConfig->cacheDirectory(__DIR__.'/storage/rector'); 30 | $rectorConfig->cacheClass(FileCacheStorage::class); 31 | 32 | // Apply sets for Laravel and general code quality 33 | $rectorConfig->sets([ 34 | LaravelLevelSetList::UP_TO_LARAVEL_110, 35 | SetList::CODE_QUALITY, 36 | ]); 37 | 38 | // Define PHP version for Rector 39 | $rectorConfig->phpVersion(PhpVersion::PHP_84); 40 | }; 41 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /.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.4' 21 | 22 | - name: Setup Node.js 23 | uses: actions/setup-node@v4 24 | with: 25 | node-version: '22' 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 'cpanel' => [ 35 | 'base_url' => env('CPANEL_BASE_URL'), 36 | 'username' => env('CPANEL_USERNAME'), 37 | 'api_token' => env('CPANEL_API_TOKEN'), 38 | ], 39 | 40 | ]; 41 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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/Providers/SocialstreamServiceProvider.php: -------------------------------------------------------------------------------- 1 | this.updateQueryString()),this.step=this.getSteps().at(h-1),this.autofocusFields()},async requestNextStep(){await this.$wire.callSchemaComponentMethod(r,"nextStep",{currentStepIndex:this.getStepIndex(this.step)})},goToNextStep(){let t=this.getStepIndex(this.step)+1;t>=this.getSteps().length||(this.step=this.getSteps()[t],this.autofocusFields(),this.scroll())},goToPreviousStep(){let t=this.getStepIndex(this.step)-1;t<0||(this.step=this.getSteps()[t],this.autofocusFields(),this.scroll())},scroll(){this.$nextTick(()=>{this.$refs.header?.children[this.getStepIndex(this.step)].scrollIntoView({behavior:"smooth",block:"start"})})},autofocusFields(){this.$nextTick(()=>this.$refs[`step-${this.step}`].querySelector("[autofocus]")?.focus())},getStepIndex(t){let e=this.getSteps().findIndex(p=>p===t);return e===-1?0:e},getSteps(){return JSON.parse(this.$refs.stepsData.value)},isFirstStep(){return this.getStepIndex(this.step)<=0},isLastStep(){return this.getStepIndex(this.step)+1>=this.getSteps().length},isStepAccessible(t){return s||this.getStepIndex(this.step)>this.getStepIndex(t)},updateQueryString(){if(!i)return;let t=new URL(window.location.href);t.searchParams.set(n,this.step),history.replaceState(null,document.title,t.toString())}}}export{o as default}; 2 | -------------------------------------------------------------------------------- /resources/views/components/confirmation-modal.blade.php: -------------------------------------------------------------------------------- 1 | @props(['id' => null, 'maxWidth' => null]) 2 | 3 | 4 |
5 |
6 |
7 | 8 | 9 | 10 |
11 | 12 |
13 |

14 | {{ $title }} 15 |

16 | 17 |
18 | {{ $content }} 19 |
20 |
21 |
22 |
23 | 24 |
25 | {{ $footer }} 26 |
27 |
28 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /resources/views/components/socialstream-icons/slack.blade.php: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/Filament/App/Pages/CreateTeam.php: -------------------------------------------------------------------------------- 1 | user()->canCreateTeams(), 403); 24 | } 25 | 26 | protected function getFormSchema(): array 27 | { 28 | return [ 29 | TextInput::make('name') 30 | ->label('Team Name') 31 | ->required() 32 | ->maxLength(255), 33 | ]; 34 | } 35 | 36 | protected function handleRegistration(array $data): Model 37 | { 38 | return app(\App\Actions\Jetstream\CreateTeam::class)->create(auth()->user(), $data); 39 | } 40 | 41 | public function getBreadcrumbs(): array 42 | { 43 | return [ 44 | url()->current() => 'Create Team', 45 | ]; 46 | } 47 | 48 | public static function getLabel(): string 49 | { 50 | return 'Create Team'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/Filament/Admin/Resources/ModuleResource/Pages/ViewModule.php: -------------------------------------------------------------------------------- 1 | label(fn () => $this->record->enabled ? 'Disable' : 'Enable') 20 | ->icon(fn () => $this->record->enabled ? 'heroicon-o-x-circle' : 'heroicon-o-check-circle') 21 | ->color(fn () => $this->record->enabled ? 'danger' : 'success') 22 | ->action(function () { 23 | $moduleManager = app(ModuleManager::class); 24 | 25 | if ($this->record->enabled) { 26 | $moduleManager->disable($this->record->name); 27 | } else { 28 | $moduleManager->enable($this->record->name); 29 | } 30 | 31 | $this->redirect(static::getResource()::getUrl('index')); 32 | }) 33 | ->requiresConfirmation(), 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.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/automation 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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /resources/views/components/contact-form.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | @csrf 4 |
5 | 6 | 7 | @error('name') 8 |
{{ $message }}
9 | @enderror 10 |
11 |
12 | 13 | 14 | @error('email') 15 |
{{ $message }}
16 | @enderror 17 |
18 |
19 | 20 | 21 | @error('message') 22 |
{{ $message }}
23 | @enderror 24 |
25 | 26 |
27 |
28 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/Models/ConnectedAccount.php: -------------------------------------------------------------------------------- 1 | 'datetime', 42 | 'expires_at' => 'datetime', 43 | ]; 44 | 45 | /** 46 | * The event map for the model. 47 | * 48 | * @var array 49 | */ 50 | protected $dispatchesEvents = [ 51 | 'created' => ConnectedAccountCreated::class, 52 | 'updated' => ConnectedAccountUpdated::class, 53 | 'deleted' => ConnectedAccountDeleted::class, 54 | ]; 55 | } 56 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Liberu 2 | APP_ENV=local 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=127.0.0.1 14 | DB_PORT=3306 15 | DB_DATABASE=test 16 | DB_USERNAME=root 17 | DB_PASSWORD= 18 | 19 | BROADCAST_DRIVER=log 20 | CACHE_DRIVER=file 21 | FILESYSTEM_DISK=local 22 | SESSION_DRIVER=file 23 | SESSION_LIFETIME=120 24 | SESSION_DRIVER=cookie 25 | SESSION_DOMAIN=null 26 | SESSION_SECURE_COOKIE=false 27 | 28 | CACHE_LIFETIME=60 29 | 30 | LOGIN_ATTEMPTS_PER_MINUTE=5 31 | PASSWORD_LIFETIME=0 32 | PASSWORD_MIN_LENGTH=6 33 | PASSWORD_MIXED_CASE=0 34 | PASSWORD_NUMERIC=0 35 | PASSWORD_SPECIAL=0 36 | 37 | REDIS_HOST=127.0.0.1 38 | REDIS_PASSWORD=null 39 | REDIS_PORT=6379 40 | 41 | MAIL_MAILER=log 42 | MAIL_HOST= 43 | MAIL_PORT= 44 | MAIL_USERNAME=null 45 | MAIL_PASSWORD=null 46 | MAIL_ENCRYPTION=null 47 | MAIL_FROM_ADDRESS=null 48 | MAIL_FROM_NAME="${APP_NAME}" 49 | 50 | MAILGUN_DOMAIN= 51 | MAILGUN_SECRET= 52 | MAILGUN_ENDPOINT=api.mailgun.net 53 | 54 | AWS_ACCESS_KEY_ID= 55 | AWS_SECRET_ACCESS_KEY= 56 | AWS_DEFAULT_REGION=us-east-1 57 | AWS_BUCKET= 58 | 59 | PUSHER_APP_ID= 60 | PUSHER_APP_KEY= 61 | PUSHER_APP_SECRET= 62 | PUSHER_APP_CLUSTER=mt1 63 | 64 | SENTRY_LARAVEL_DSN= 65 | 66 | TELESCOPE_ENABLED=0 67 | SCOUT_DRIVER=null 68 | 69 | ENSO_API_TOKEN= 70 | 71 | TINY_MCE_API_KEY= 72 | 73 | SANCTUM_STATEFUL_DOMAINS=localhost,127.0.0.1,127.0.0.1:8000,127.0.0.1:3000,localhost:3000,localhost:8080,::1 74 | -------------------------------------------------------------------------------- /resources/views/auth/forgot-password.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
5 |
6 | 7 |
8 | {{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }} 9 |
10 | 11 | @session('status') 12 |
13 | {{ $value }} 14 |
15 | @endsession 16 | 17 | 18 | 19 |
20 | @csrf 21 | 22 |
23 | 24 | 26 |
27 | 28 |
29 | 30 | {{ __('Email Password Reset Link') }} 31 | 32 |
33 |
34 |
35 |
36 | 37 | @endsection 38 | -------------------------------------------------------------------------------- /app/Services/PleskApiClient.php: -------------------------------------------------------------------------------- 1 | baseUrl = $baseUrl; 16 | $this->username = $username; 17 | $this->password = $password; 18 | } 19 | 20 | public function createAccount(array $data): bool 21 | { 22 | // Implement Plesk API call to create an account 23 | // Return true if successful, false otherwise 24 | } 25 | 26 | public function suspendAccount(string $accountId): bool 27 | { 28 | // Implement Plesk API call to suspend an account 29 | // Return true if successful, false otherwise 30 | } 31 | 32 | public function unsuspendAccount(string $accountId): bool 33 | { 34 | // Implement Plesk API call to unsuspend an account 35 | // Return true if successful, false otherwise 36 | } 37 | 38 | public function deleteAccount(string $accountId): bool 39 | { 40 | // Implement Plesk API call to delete an account 41 | // Return true if successful, false otherwise 42 | } 43 | 44 | private function makeApiRequest(string $endpoint, array $data = []): array 45 | { 46 | // Implement the API request logic here 47 | // This method should handle authentication and make the actual HTTP request to the Plesk API 48 | // Return the API response as an array 49 | } 50 | } -------------------------------------------------------------------------------- /app/Http/Controllers/ResetPasswordController.php: -------------------------------------------------------------------------------- 1 | $token, 'email' => $request->email]); 17 | } 18 | 19 | public function reset(Request $request) 20 | { 21 | $request->validate([ 22 | 'token' => 'required', 23 | 'email' => 'required|email', 24 | 'password' => 'required|min:8|confirmed', 25 | ]); 26 | 27 | $status = Password::broker('admins')->reset( 28 | $request->only('email', 'password', 'password_confirmation', 'token'), 29 | function ($user, $password) { 30 | $user->forceFill([ 31 | 'password' => Hash::make($password), 32 | ])->setRememberToken(Str::random(60)); 33 | 34 | $user->save(); 35 | 36 | event(new PasswordReset($user)); 37 | } 38 | ); 39 | 40 | return $status === Password::PASSWORD_RESET 41 | ? redirect()->route('admin.login')->with('status', __($status)) 42 | : back()->withErrors(['email' => [__($status)]]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/Providers/FortifyServiceProvider.php: -------------------------------------------------------------------------------- 1 | input(Fortify::username())).'|'.$request->ip()); 38 | 39 | return Limit::perMinute(5)->by($throttleKey); 40 | }); 41 | 42 | RateLimiter::for('two-factor', function (Request $request) { 43 | return Limit::perMinute(5)->by($request->session()->get('login.id')); 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /resources/views/components/socialstream-icons/provider-icon.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @switch($provider) 3 | @case(\JoelButcher\Socialstream\Providers::bitbucket()) 4 | 5 | @break 6 | 7 | @case (JoelButcher\Socialstream\Providers::facebook()) 8 | 9 | @break 10 | 11 | @case (JoelButcher\Socialstream\Providers::github()) 12 | 13 | @break 14 | 15 | @case (JoelButcher\Socialstream\Providers::gitlab()) 16 | 17 | @break 18 | 19 | @case (JoelButcher\Socialstream\Providers::google()) 20 | 21 | @break 22 | 23 | @case (JoelButcher\Socialstream\Providers::linkedin()) 24 | @case (JoelButcher\Socialstream\Providers::linkedinOpenId()) 25 | 26 | @break 27 | 28 | @case (JoelButcher\Socialstream\Providers::slack()) 29 | 30 | @break 31 | 32 | @case (JoelButcher\Socialstream\Providers::twitterOAuth1()) 33 | @case (JoelButcher\Socialstream\Providers::twitterOAuth2()) 34 | @case (JoelButcher\Socialstream\Providers::twitter()) 35 | 36 | @break 37 | @endswitch 38 |
39 | -------------------------------------------------------------------------------- /resources/views/auth/reset-password.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | @csrf 11 | 12 | 13 | 14 |
15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 |
23 | 24 |
25 | 26 | 27 |
28 | 29 |
30 | 31 | {{ __('Reset Password') }} 32 | 33 |
34 |
35 |
36 |
37 | -------------------------------------------------------------------------------- /public/js/filament/forms/components/checkbox-list.js: -------------------------------------------------------------------------------- 1 | function c({livewireId:s}){return{areAllCheckboxesChecked:!1,checkboxListOptions:[],search:"",visibleCheckboxListOptions:[],init(){this.checkboxListOptions=Array.from(this.$root.querySelectorAll(".fi-fo-checkbox-list-option")),this.updateVisibleCheckboxListOptions(),this.$nextTick(()=>{this.checkIfAllCheckboxesAreChecked()}),Livewire.hook("commit",({component:e,commit:t,succeed:i,fail:o,respond:h})=>{i(({snapshot:r,effect:l})=>{this.$nextTick(()=>{e.id===s&&(this.checkboxListOptions=Array.from(this.$root.querySelectorAll(".fi-fo-checkbox-list-option")),this.updateVisibleCheckboxListOptions(),this.checkIfAllCheckboxesAreChecked())})})}),this.$watch("search",()=>{this.updateVisibleCheckboxListOptions(),this.checkIfAllCheckboxesAreChecked()})},checkIfAllCheckboxesAreChecked(){this.areAllCheckboxesChecked=this.visibleCheckboxListOptions.length===this.visibleCheckboxListOptions.filter(e=>e.querySelector("input[type=checkbox]:checked, input[type=checkbox]:disabled")).length},toggleAllCheckboxes(){this.checkIfAllCheckboxesAreChecked();let e=!this.areAllCheckboxesChecked;this.visibleCheckboxListOptions.forEach(t=>{let i=t.querySelector("input[type=checkbox]");i.disabled||i.checked!==e&&(i.checked=e,i.dispatchEvent(new Event("change")))}),this.areAllCheckboxesChecked=e},updateVisibleCheckboxListOptions(){this.visibleCheckboxListOptions=this.checkboxListOptions.filter(e=>["",null,void 0].includes(this.search)||e.querySelector(".fi-fo-checkbox-list-option-label")?.innerText.toLowerCase().includes(this.search.toLowerCase())?!0:e.querySelector(".fi-fo-checkbox-list-option-description")?.innerText.toLowerCase().includes(this.search.toLowerCase()))}}}export{c as default}; 2 | -------------------------------------------------------------------------------- /app/Services/MenuService.php: -------------------------------------------------------------------------------- 1 | orderBy('order')->get(); 14 | 15 | $menu = SpatieMenu::new() 16 | ->addClass('flex items-center space-x-4') 17 | ->addItemClass('px-4 py-2 rounded-md bg-green-700 text-white hover:bg-green-600 transition duration-300 ease-in-out'); 18 | 19 | $this->createMenuItems($menuItems)->each(function ($item) use ($menu) { 20 | $menu->add($item); 21 | }); 22 | 23 | return $menu; 24 | } 25 | 26 | private function createMenuItems($items) 27 | { 28 | return $items->map(function ($item) { 29 | if ($item->children->count() > 0) { 30 | $submenu = SpatieMenu::new() 31 | ->addClass('absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1') 32 | ->addItemClass('block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100'); 33 | 34 | $this->createMenuItems($item->children)->each(function ($subItem) use ($submenu) { 35 | $submenu->add($subItem); 36 | }); 37 | 38 | return SpatieMenu::new() 39 | ->add(Link::to($item->url, $item->name)->addClass('relative group')) 40 | ->add($submenu->addClass('hidden group-hover:block')); 41 | } 42 | 43 | return Link::to($item->url, $item->name); 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/Filament/App/Pages/EditProfile.php: -------------------------------------------------------------------------------- 1 | user = Auth::user(); 21 | $this->form->fill([ 22 | 'name' => $this->user->name, 23 | 'email' => $this->user->email, 24 | ]); 25 | } 26 | 27 | protected function getFormSchema(): array 28 | { 29 | return [ 30 | TextInput::make('name') 31 | ->label('Name') 32 | ->required() 33 | ->maxLength(255), 34 | TextInput::make('email') 35 | ->label('Email Address') 36 | ->required() 37 | ->maxLength(255), 38 | ]; 39 | } 40 | 41 | public function submit() 42 | { 43 | $this->validate(); 44 | 45 | $state = $this->form->getState(); 46 | 47 | $this->user->forceFill([ 48 | 'name' => $state['name'], 49 | 'email' => $state['email'], 50 | ])->save(); 51 | 52 | Filament::notify('success', 'Your profile has been updated.'); 53 | } 54 | 55 | public function getBreadcrumbs(): array 56 | { 57 | return [ 58 | url()->current() => 'Edit Profile', 59 | ]; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/Filament/App/Pages/EditTeam.php: -------------------------------------------------------------------------------- 1 | user()->canCreateTeams(), 403); 25 | } 26 | 27 | protected function getFormSchema(): array 28 | { 29 | return [ 30 | TextInput::make('name') 31 | ->label('Team Name') 32 | ->required() 33 | ->maxLength(255), 34 | ]; 35 | } 36 | 37 | public function submit() 38 | { 39 | $this->validate(); 40 | 41 | $team = Team::forceCreate([ 42 | 'user_id' => Filament::auth()->id(), 43 | 'name' => $this->name, 44 | 'personal_team' => false, 45 | ]); 46 | 47 | $this->user()->teams()->attach($team, ['role' => 'admin']); 48 | $this->user()->switchTeam($team); 49 | 50 | return redirect()->route('filament.pages.edit-team', ['team' => $team]); 51 | } 52 | 53 | public function getBreadcrumbs(): array 54 | { 55 | return [ 56 | url()->current() => 'Create Team', 57 | ]; 58 | } 59 | 60 | private function user(): User 61 | { 62 | return Filament::auth()->user(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/Http/Controllers/TeamInvitationController.php: -------------------------------------------------------------------------------- 1 | validate([ 17 | 'email' => 'required|email', 18 | 'team_id' => 'required|exists:teams,id', 19 | ]); 20 | 21 | $user = User::firstOrCreate(['email' => $request->email], ['password' => bcrypt(Str::random(10))]); 22 | $team = Team::findOrFail($request->team_id); 23 | 24 | $invitationToken = Str::random(32); 25 | $user->invitations()->create([ 26 | 'team_id' => $team->id, 27 | 'token' => $invitationToken, 28 | ]); 29 | 30 | Mail::to($request->email)->send(new TeamInvitation($user, $team, $invitationToken)); 31 | 32 | return response()->json(['message' => 'Invitation sent successfully.']); 33 | } 34 | 35 | public function acceptInvitation(Request $request) 36 | { 37 | $request->validate([ 38 | 'token' => 'required|exists:invitations,token', 39 | ]); 40 | 41 | $invitation = Invitation::where('token', $request->token)->firstOrFail(); 42 | $team = Team::findOrFail($invitation->team_id); 43 | $team->members()->attach($invitation->user_id); 44 | 45 | $invitation->update(['accepted' => true]); 46 | 47 | return response()->json(['message' => 'Invitation accepted successfully.']); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.docker/octane/Swoole/supervisord.swoole.conf: -------------------------------------------------------------------------------- 1 | [program:octane] 2 | process_name=%(program_name)s_%(process_num)02d 3 | command=php %(ENV_ROOT)s/artisan octane:start --server=swoole --host=0.0.0.0 --port=8000 4 | user=%(ENV_USER)s 5 | autostart=true 6 | autorestart=true 7 | environment=LARAVEL_OCTANE="1" 8 | stdout_logfile=/dev/stdout 9 | stdout_logfile_maxbytes=0 10 | stderr_logfile=/dev/stderr 11 | stderr_logfile_maxbytes=0 12 | 13 | [program:horizon] 14 | process_name=%(program_name)s_%(process_num)02d 15 | command=php %(ENV_ROOT)s/artisan horizon 16 | user=%(ENV_USER)s 17 | autostart=%(ENV_WITH_HORIZON)s 18 | autorestart=true 19 | stdout_logfile=%(ENV_ROOT)s/storage/logs/horizon.log 20 | stdout_logfile_maxbytes=200MB 21 | stderr_logfile=%(ENV_ROOT)s/storage/logs/horizon.log 22 | stderr_logfile_maxbytes=200MB 23 | stopwaitsecs=3600 24 | 25 | [program:scheduler] 26 | process_name=%(program_name)s_%(process_num)02d 27 | command=supercronic -overlapping /etc/supercronic/laravel 28 | user=%(ENV_USER)s 29 | autostart=%(ENV_WITH_SCHEDULER)s 30 | autorestart=true 31 | stdout_logfile=%(ENV_ROOT)s/storage/logs/scheduler.log 32 | stdout_logfile_maxbytes=200MB 33 | stderr_logfile=%(ENV_ROOT)s/storage/logs/scheduler.log 34 | stderr_logfile_maxbytes=200MB 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 | startsecs=0 43 | startretries=1 44 | stdout_logfile=%(ENV_ROOT)s/storage/logs/scheduler.log 45 | stdout_logfile_maxbytes=200MB 46 | stderr_logfile=%(ENV_ROOT)s/storage/logs/scheduler.log 47 | stderr_logfile_maxbytes=200MB 48 | 49 | [include] 50 | files=/etc/supervisor/supervisord.conf 51 | -------------------------------------------------------------------------------- /app/Actions/Jetstream/RemoveTeamMember.php: -------------------------------------------------------------------------------- 1 | authorize($user, $team, $teamMember); 21 | 22 | $this->ensureUserDoesNotOwnTeam($teamMember, $team); 23 | 24 | $team->removeUser($teamMember); 25 | 26 | TeamMemberRemoved::dispatch($team, $teamMember); 27 | } 28 | 29 | /** 30 | * Authorize that the user can remove the team member. 31 | */ 32 | protected function authorize(User $user, Team $team, User $teamMember): void 33 | { 34 | if (!Gate::forUser($user)->check('removeTeamMember', $team) && 35 | $user->id !== $teamMember->id) { 36 | throw new AuthorizationException(); 37 | } 38 | } 39 | 40 | /** 41 | * Ensure that the currently authenticated user does not own the team. 42 | */ 43 | protected function ensureUserDoesNotOwnTeam(User $teamMember, Team $team): void 44 | { 45 | if ($teamMember->id === $team->owner->id) { 46 | throw ValidationException::withMessages([ 47 | 'team' => [__('You may not leave a team that you created.')], 48 | ])->errorBag('removeTeamMember'); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/Filament/App/Widgets/SocialLinksWidget.php: -------------------------------------------------------------------------------- 1 | github_url) { 22 | $links['GitHub'] = $settings->github_url; 23 | } 24 | 25 | if ($settings->facebook_url) { 26 | $links['Facebook'] = $settings->facebook_url; 27 | } 28 | 29 | if ($settings->twitter_url) { 30 | $links['Twitter'] = $settings->twitter_url; 31 | } 32 | 33 | if ($settings->youtube_url) { 34 | $links['YouTube'] = $settings->youtube_url; 35 | } 36 | 37 | // Keep the Facebook Groups as fallback 38 | if (empty($links)) { 39 | $links = [ 40 | 'GitHub' => 'https://www.github.com/liberu-genealogy', 41 | 'Facebook Page' => 'https://www.facebook.com/familytree365', 42 | 'Facebook Groups' => [ 43 | 'Family Tree 365' => 'https://www.facebook.com/groups/familytree365', 44 | 'Genealogy Chat' => 'https://www.facebook.com/groups/genealogychat', 45 | 'DNA 365' => 'https://www.facebook.com/groups/dna365', 46 | ], 47 | ]; 48 | } 49 | 50 | return view($this->view, [ 51 | 'links' => $links, 52 | ]); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/Actions/Fortify/ResetUserPassword.php: -------------------------------------------------------------------------------- 1 | $input 21 | * @throws ValidationException 22 | * @throws Exception 23 | */ 24 | public function reset(User $user, array $input): void 25 | { 26 | try { 27 | Validator::make($input, [ 28 | 'password' => $this->passwordRules(), 29 | ])->validate(); 30 | 31 | $user->forceFill([ 32 | 'password' => Hash::make($input['password']), 33 | ])->save(); 34 | 35 | Log::info('User password reset successfully', ['user_id' => $user->id]); 36 | } catch (ValidationException $e) { 37 | Log::error('Password reset validation failed', [ 38 | 'user_id' => $user->id, 39 | 'errors' => $e->errors(), 40 | ]); 41 | throw $e; 42 | } catch (Exception $e) { 43 | Log::error('Password reset failed', [ 44 | 'user_id' => $user->id, 45 | 'message' => $e->getMessage(), 46 | 'trace' => $e->getTraceAsString(), 47 | ]); 48 | throw new Exception('Failed to reset password. Please try again later.'); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /resources/views/components/dropdown.blade.php: -------------------------------------------------------------------------------- 1 | @props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white', 'dropdownClasses' => '']) 2 | 3 | @php 4 | switch ($align) { 5 | case 'left': 6 | $alignmentClasses = 'ltr:origin-top-left rtl:origin-top-right start-0'; 7 | break; 8 | case 'top': 9 | $alignmentClasses = 'origin-top'; 10 | break; 11 | case 'none': 12 | case 'false': 13 | $alignmentClasses = ''; 14 | break; 15 | case 'right': 16 | default: 17 | $alignmentClasses = 'ltr:origin-top-right rtl:origin-top-left end-0'; 18 | break; 19 | } 20 | 21 | switch ($width) { 22 | case '48': 23 | $width = 'w-48'; 24 | break; 25 | } 26 | @endphp 27 | 28 |
29 |
30 | {{ $trigger }} 31 |
32 | 33 | 47 |
48 | -------------------------------------------------------------------------------- /.docker/config/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | # Default server definition 2 | server { 3 | listen [::]:80 default_server; 4 | listen 80 default_server; 5 | server_name _; 6 | 7 | sendfile off; 8 | tcp_nodelay on; 9 | absolute_redirect off; 10 | 11 | root /var/www/html; 12 | index index.php index.html; 13 | 14 | location / { 15 | # First attempt to serve request as file, then 16 | # as directory, then fall back to index.php 17 | try_files $uri $uri/ /index.php?q=$uri&$args; 18 | } 19 | 20 | # Redirect server error pages to the static page /50x.html 21 | error_page 500 502 503 504 /50x.html; 22 | location = /50x.html { 23 | root /var/lib/nginx/html; 24 | } 25 | 26 | # Pass the PHP scripts to PHP-FPM listening on php-fpm.sock 27 | location ~ \.php$ { 28 | try_files $uri =404; 29 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 30 | fastcgi_pass unix:/run/php-fpm.sock; 31 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 32 | fastcgi_index index.php; 33 | include fastcgi_params; 34 | } 35 | 36 | # Set the cache-control headers on assets to cache for 5 days 37 | location ~* \.(jpg|jpeg|gif|png|css|js|ico|xml)$ { 38 | expires 5d; 39 | } 40 | 41 | # Deny access to . files, for security 42 | location ~ /\. { 43 | log_not_found off; 44 | deny all; 45 | } 46 | 47 | # Allow fpm ping and status from localhost 48 | location ~ ^/(fpm-status|fpm-ping)$ { 49 | access_log off; 50 | allow 127.0.0.1; 51 | deny all; 52 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 53 | include fastcgi_params; 54 | fastcgi_pass unix:/run/php-fpm.sock; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.docker/config/config/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | # Default server definition 2 | server { 3 | listen [::]:8080 default_server; 4 | listen 8080 default_server; 5 | server_name _; 6 | 7 | sendfile off; 8 | tcp_nodelay on; 9 | absolute_redirect off; 10 | 11 | root /var/www/html; 12 | index index.php index.html; 13 | 14 | location / { 15 | # First attempt to serve request as file, then 16 | # as directory, then fall back to index.php 17 | try_files $uri $uri/ /index.php?q=$uri&$args; 18 | } 19 | 20 | # Redirect server error pages to the static page /50x.html 21 | error_page 500 502 503 504 /50x.html; 22 | location = /50x.html { 23 | root /var/lib/nginx/html; 24 | } 25 | 26 | # Pass the PHP scripts to PHP-FPM listening on php-fpm.sock 27 | location ~ \.php$ { 28 | try_files $uri =404; 29 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 30 | fastcgi_pass unix:/run/php-fpm.sock; 31 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 32 | fastcgi_index index.php; 33 | include fastcgi_params; 34 | } 35 | 36 | # Set the cache-control headers on assets to cache for 5 days 37 | location ~* \.(jpg|jpeg|gif|png|css|js|ico|xml)$ { 38 | expires 5d; 39 | } 40 | 41 | # Deny access to . files, for security 42 | location ~ /\. { 43 | log_not_found off; 44 | deny all; 45 | } 46 | 47 | # Allow fpm ping and status from localhost 48 | location ~ ^/(fpm-status|fpm-ping)$ { 49 | access_log off; 50 | allow 127.0.0.1; 51 | deny all; 52 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 53 | include fastcgi_params; 54 | fastcgi_pass unix:/run/php-fpm.sock; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/Actions/Fortify/CreateNewUserWithTeams.php: -------------------------------------------------------------------------------- 1 | $input 21 | */ 22 | public function create(array $input): User 23 | { 24 | Validator::make($input, [ 25 | 'name' => ['required', 'string', 'max:255'], 26 | 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 27 | 'password' => $this->passwordRules(), 28 | 'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature() ? ['accepted', 'required'] : '', 29 | ])->validate(); 30 | 31 | return DB::transaction(function () use ($input) { 32 | return tap(User::create([ 33 | 'name' => $input['name'], 34 | 'email' => $input['email'], 35 | 'password' => Hash::make($input['password']), 36 | ]), function (User $user) { 37 | $this->createTeam($user); 38 | }); 39 | }); 40 | } 41 | 42 | /** 43 | * Create teams for the user. 44 | */ 45 | protected function createTeam(User $user): void 46 | { 47 | $teamManagementService = app(TeamManagementService::class); 48 | $teamManagementService->assignUserToDefaultTeam($user); 49 | $teamManagementService->createPersonalTeamForUser($user); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/Actions/Socialstream/CreateUserFromProvider.php: -------------------------------------------------------------------------------- 1 | createsConnectedAccounts = $createsConnectedAccounts; 25 | } 26 | 27 | /** 28 | * Create a new user from a social provider user. 29 | */ 30 | public function create(string $provider, ProviderUser $providerUser): User 31 | { 32 | return DB::transaction(function () use ($provider, $providerUser) { 33 | return tap(User::create([ 34 | 'name' => $providerUser->getName() ?? $providerUser->getNickname(), 35 | 'email' => $providerUser->getEmail(), 36 | ]), function (User $user) use ($provider, $providerUser) { 37 | $user->markEmailAsVerified(); 38 | 39 | if (Socialstream::hasProviderAvatarsFeature() && $providerUser->getAvatar()) { 40 | $user->setProfilePhotoFromUrl($providerUser->getAvatar()); 41 | } 42 | 43 | $this->createsConnectedAccounts->create($user, $provider, $providerUser); 44 | }); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/Providers/TeamServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->bind(\FamilyTree365\LaravelGedcom\Utils\BatchData::class, BatchData::class); 26 | $this->app->bind(\FamilyTree365\LaravelGedcom\Models\Family::class, Family::class); 27 | $this->app->bind(\FamilyTree365\LaravelGedcom\Models\FamilyEvent::class, FamilyEvent::class); 28 | $this->app->bind(\FamilyTree365\LaravelGedcom\Models\FamilySlgs::class, FamilySlgs::class); 29 | $this->app->bind(\FamilyTree365\LaravelGedcom\Models\Person::class, Person::class); 30 | $this->app->bind(\FamilyTree365\LaravelGedcom\Models\PersonAsso::class, PersonAsso::class); 31 | $this->app->bind(\FamilyTree365\LaravelGedcom\Models\PersonAlia::class, PersonAlia::class); 32 | $this->app->bind(\FamilyTree365\LaravelGedcom\Models\PersonEvent::class, PersonEvent::class); 33 | $this->app->bind(\FamilyTree365\LaravelGedcom\Models\Addr::class, Addr::class); 34 | $this->app->bind(\FamilyTree365\LaravelGedcom\Models\Chan::class, Chan::class); 35 | $this->app->bind(\FamilyTree365\LaravelGedcom\Models\Subm::class, Subm::class); 36 | } 37 | 38 | /** 39 | * Bootstrap services. 40 | */ 41 | public function boot(): void 42 | { 43 | // 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /resources/views/teams/delete-team-form.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ __('Delete Team') }} 4 | 5 | 6 | 7 | {{ __('Permanently delete this team.') }} 8 | 9 | 10 | 11 |
12 | {{ __('Once a team is deleted, all of its resources and data will be permanently deleted. Before deleting this team, please download any data or information regarding this team that you wish to retain.') }} 13 |
14 | 15 |
16 | 17 | {{ __('Delete Team') }} 18 | 19 |
20 | 21 | 22 | 23 | 24 | {{ __('Delete Team') }} 25 | 26 | 27 | 28 | {{ __('Are you sure you want to delete this team? Once a team is deleted, all of its resources and data will be permanently deleted.') }} 29 | 30 | 31 | 32 | 33 | {{ __('Cancel') }} 34 | 35 | 36 | 37 | {{ __('Delete Team') }} 38 | 39 | 40 | 41 |
42 |
43 | --------------------------------------------------------------------------------