├── http ├── api_numbers.http ├── http-client.env.json.dist └── myfleet_create_ship.http ├── config ├── packages │ ├── monolog.yaml │ ├── test │ │ ├── twig.yaml │ │ ├── debug.yaml │ │ ├── routing.yaml │ │ ├── validator.yaml │ │ ├── web_profiler.yaml │ │ ├── framework.php │ │ ├── jwt_auth.yaml │ │ └── monolog.yaml │ ├── assets.yaml │ ├── beta │ │ ├── sentry.yaml │ │ ├── doctrine.yaml │ │ ├── routing.yaml │ │ └── monolog.yaml │ ├── dev │ │ ├── debug.yaml │ │ ├── routing.yaml │ │ ├── hautelook_alice.yaml │ │ ├── web_profiler.yaml │ │ ├── nelmio_alice.yaml │ │ ├── nelmio_api_doc.yaml │ │ └── monolog.yaml │ ├── prod │ │ ├── routing.yaml │ │ ├── doctrine.yaml │ │ └── sentry.yaml │ ├── validator.yaml │ ├── sensio_framework_extra.yaml │ ├── http_client.yaml │ ├── mailer.yaml │ ├── doctrine_migrations.yaml │ ├── test_acceptance │ │ ├── import_test.yaml │ │ └── messenger.yaml │ ├── routing.yaml │ ├── translation.yaml │ ├── notifier.yaml │ ├── twig.yaml │ ├── cache.yaml │ ├── framework.yaml │ ├── httplug.yaml │ ├── doctrine.yaml │ ├── security.yaml │ ├── psr_http_message_bridge.yaml │ ├── nyholm_psr7.yaml │ ├── messenger.yaml │ └── jwt_auth.yaml ├── routes │ ├── dev │ │ ├── framework.yaml │ │ ├── web_profiler.yaml │ │ └── nelmio_api_doc.yaml │ └── annotations.yaml ├── preload.php ├── services_test.yaml └── bundles.php ├── .dockerignore ├── public ├── favicon.ico └── index.php ├── src ├── Domain │ ├── OrgaId.php │ ├── ShipId.php │ ├── UserId.php │ ├── MemberId.php │ ├── FundingId.php │ ├── PatchNoteId.php │ ├── ShipTemplateId.php │ ├── TemplateAuthorId.php │ ├── OrganizationShipId.php │ ├── MonthlyCostCoverageId.php │ ├── MyFleet │ │ ├── FleetShipImport.php │ │ └── UserShipTemplate.php │ ├── Service │ │ └── EntityIdGeneratorInterface.php │ ├── Event │ │ ├── DeletedUserEvent.php │ │ ├── UpdatedShip.php │ │ └── UpdatedFleetEvent.php │ ├── MemberProfile.php │ ├── UserFleet.php │ ├── UserProfile.php │ ├── Exception │ │ ├── DomainException.php │ │ ├── NotFoundOrganizationException.php │ │ ├── NotFoundFleetByUserException.php │ │ ├── ConflictVersionException.php │ │ ├── FailedValidationException.php │ │ ├── NotFoundUserException.php │ │ ├── NotFoundOrganizationFleetException.php │ │ ├── AlreadyExistingFleetForUserException.php │ │ ├── NoMemberHandleException.php │ │ ├── NotFoundShipException.php │ │ ├── NotFounderOfOrganizationException.php │ │ ├── NotFoundShipTemplateByUserException.php │ │ ├── FounderOfOrganizationException.php │ │ ├── NotJoinedOrganizationMemberException.php │ │ ├── AlreadyMemberOfOrganizationException.php │ │ ├── NotMemberOfOrganizationException.php │ │ └── FullyJoinedMemberOfOrganizationException.php │ ├── EntityId.php │ ├── UserShip.php │ └── Notification │ │ └── FeedbackNotification.php ├── Application │ ├── Common │ │ └── Clock.php │ ├── Repository │ │ ├── Auth0RepositoryInterface.php │ │ ├── PatchNoteRepositoryInterface.php │ │ ├── ShipTemplateRepositoryInterface.php │ │ ├── FleetRepositoryInterface.php │ │ ├── UserRepositoryInterface.php │ │ ├── OrganizationFleetRepositoryInterface.php │ │ └── OrganizationRepositoryInterface.php │ ├── PatchNote │ │ ├── Output │ │ │ ├── HasNewPatchNoteOutput.php │ │ │ ├── LastPatchNotesOutput.php │ │ │ └── LastPatchNoteOutput.php │ │ └── HasNewPatchNoteService.php │ ├── Home │ │ ├── Output │ │ │ └── NumbersOutput.php │ │ └── NumbersService.php │ ├── Provider │ │ ├── UserFleetProviderInterface.php │ │ ├── ListTemplatesProviderInterface.php │ │ └── MemberProfileProviderInterface.php │ ├── MyFleet │ │ ├── Input │ │ │ └── ImportFleetShip.php │ │ ├── Output │ │ │ ├── MyFleetOutput.php │ │ │ ├── MyFleetShipsCollectionOutput.php │ │ │ └── MyFleetShipOutput.php │ │ ├── DeleteAccountHandler.php │ │ ├── ClearMyFleetService.php │ │ ├── CreateShipService.php │ │ ├── DeleteShipService.php │ │ ├── UpdateShipService.php │ │ ├── MyFleetService.php │ │ ├── ImportFleetService.php │ │ └── UpdateShipFromTemplateService.php │ ├── ShipTemplate │ │ ├── Output │ │ │ ├── ListTemplatesOutput.php │ │ │ ├── CargoCapacityOutput.php │ │ │ ├── ShipChassisOutput.php │ │ │ ├── CrewOutput.php │ │ │ ├── ManufacturerOutput.php │ │ │ ├── PriceOutput.php │ │ │ └── ListTemplatesItemOutput.php │ │ ├── Input │ │ │ └── CreateTemplateInput.php │ │ └── CreateTemplateService.php │ ├── MyOrganizations │ │ ├── Output │ │ │ ├── MyOrganizationsOutput.php │ │ │ ├── OrganizationMembersOutput.php │ │ │ ├── OrganizationShipOwnersOutput.php │ │ │ ├── OrganizationCandidatesOutput.php │ │ │ ├── OrganizationsCollectionOutput.php │ │ │ ├── OrganizationsItemFleetOutput.php │ │ │ ├── OrganizationMembersItemOutput.php │ │ │ ├── OrganizationCandidatesItemOutput.php │ │ │ ├── OrganizationShipOwnersItemOutput.php │ │ │ ├── OrganizationsItemOutput.php │ │ │ ├── OrganizationsItemFleetShipsOutput.php │ │ │ ├── MyOrganizationsItemOutput.php │ │ │ └── OrganizationsItemWithFleetOutput.php │ │ ├── UpdateOrganizationService.php │ │ ├── DisbandOrganizationService.php │ │ ├── MyOrganizationsService.php │ │ ├── UnjoinOrganizationService.php │ │ └── OrganizationsService.php │ ├── Profile │ │ ├── Output │ │ │ └── PublicProfileOutput.php │ │ ├── DeleteAccountHandler.php │ │ ├── ChangeHandleService.php │ │ ├── ChangeNicknameService.php │ │ ├── SavePreferencesService.php │ │ ├── DeleteAccountService.php │ │ ├── PublicProfilesService.php │ │ └── ProfileService.php │ └── Support │ │ └── GiveFeedbackService.php ├── Form │ ├── Dto │ │ ├── PayPalCaptureTransaction.php │ │ ├── PatchNote.php │ │ ├── FundingPayment.php │ │ └── MonthlyCostCoverage.php │ └── PatchNoteForm.php ├── Infrastructure │ ├── Common │ │ ├── SystemClock.php │ │ └── FakeClock.php │ ├── Security │ │ └── FakeAuth0Service.php │ ├── Controller │ │ ├── Profile │ │ │ ├── Input │ │ │ │ ├── SavePreferencesInput.php │ │ │ │ ├── ChangeNicknameInput.php │ │ │ │ └── ChangeHandleInput.php │ │ │ └── DeleteAccountController.php │ │ ├── ShipTemplate │ │ │ └── Input │ │ │ │ ├── CreateTemplateChassisInput.php │ │ │ │ ├── CreateTemplatePriceInput.php │ │ │ │ ├── CreateTemplateCrewInput.php │ │ │ │ └── CreateTemplateManufacturerInput.php │ │ ├── MyFleet │ │ │ ├── Input │ │ │ │ ├── UpdateShipFromTemplateInput.php │ │ │ │ └── CreateShipFromTemplateInput.php │ │ │ └── ClearMyFleetController.php │ │ └── Home │ │ │ └── NumbersController.php │ ├── Service │ │ ├── SystemEntityIdGenerator.php │ │ └── InMemoryEntityIdGenerator.php │ ├── Repository │ │ ├── User │ │ │ ├── FakeAuth0Repository.php │ │ │ └── Auth0Repository.php │ │ └── ShipTemplate │ │ │ └── InMemoryShipTemplateRepository.php │ ├── Provider │ │ ├── Organizations │ │ │ ├── InMemoryUserFleetProvider.php │ │ │ ├── InMemoryMemberProfileProvider.php │ │ │ └── DirectCallMemberProfileProvider.php │ │ └── MyFleet │ │ │ ├── InMemoryListTemplatesProvider.php │ │ │ └── DirectCallListTemplatesProvider.php │ ├── Validator │ │ ├── UniqueOrganizationSid.php │ │ ├── CountShipsLessThan.php │ │ ├── CountOrganizationsLessThan.php │ │ ├── UniqueUserHandle.php │ │ ├── UniqueOrganizationSidValidator.php │ │ └── CountOrganizationsLessThanValidator.php │ └── Listener │ │ └── FormatExceptionListener.php ├── Message │ └── Funding │ │ ├── SendOrderRefundMail.php │ │ └── SendOrderCaptureSummaryMail.php ├── Entity │ ├── VersionnableTrait.php │ ├── ShipChassis.php │ ├── CargoCapacity.php │ ├── ShipRole.php │ ├── ShipSize.php │ ├── Price.php │ ├── Manufacturer.php │ ├── Crew.php │ └── Membership.php ├── Event │ └── FundingUpdatedEvent.php ├── Exception │ └── UnableToCreatePaypalOrderException.php ├── Repository │ └── MonthlyCostCoverageRepository.php ├── Security │ └── ApiAccessDeniedHandler.php ├── Service │ └── Funding │ │ ├── PaypalCheckoutInterface.php │ │ └── VerifyWebhookSignatureFactory.php ├── Validator │ └── Constraints │ │ ├── UniqueField.php │ │ └── UniqueFieldValidator.php ├── Controller │ ├── Funding │ │ ├── ConfigurationController.php │ │ └── MyBackingsController.php │ └── BackOffice │ │ └── PatchNote │ │ └── PatchNoteListController.php ├── Command │ └── DeleteOldCreatedFundingCommand.php ├── Listener │ └── Funding │ │ └── UpdateSupporterAdvantagesListener.php ├── Kernel.php └── Serializer │ └── EntityIdNormalizer.php ├── docker ├── php │ ├── entrypoint.sh │ ├── php-fpm.dev.conf │ ├── php-fpm.prod.conf │ ├── php-cli.dev.ini │ ├── php-cli.prod.ini │ ├── www.dev.conf │ ├── www.prod.conf │ ├── php-fpm-fcgi.prod.ini │ └── php-fpm-fcgi.dev.ini ├── supervisor │ ├── supervisord.conf │ ├── app.conf │ └── app.prod.conf └── mysql │ └── config-file.cnf ├── templates ├── back_office_layout.html.twig ├── emails │ └── order_capture_summary.txt.twig └── back_office │ ├── patch_note │ ├── create.html.twig │ └── edit.html.twig │ └── funding │ └── monthly_cost_coverage_create.html.twig ├── tests ├── bootstrap.php ├── Acceptance │ └── KernelTestCase.php ├── Integration │ └── KernelTestCase.php ├── Service │ └── PayPal │ │ └── MockVerifyWebhookSignatureFactory.php ├── End2End │ └── Controller │ │ └── PatchNote │ │ └── HasNewPatchNoteControllerTest.php └── Poubelle │ └── BackOffice │ └── PatchNote │ └── PatchNoteCreateControllerTest.php ├── .editorconfig ├── bin ├── phpunit └── console ├── .env.test ├── .gitignore ├── migrations ├── Version20210425163351.php ├── Version20210407181655.php ├── Version20210502100109.php ├── Version20210505165223.php ├── Version20210523102656.php ├── Version20210410225927.php ├── Version20210413195714.php ├── Version20210412224435.php ├── Version20210522155300.php └── Version20210506210323.php ├── docker-compose.yml ├── README.md ├── fixtures ├── patch_note.yaml └── monthly_cost_coverage.yaml ├── phpunit.xml.dist └── .env /http/api_numbers.http: -------------------------------------------------------------------------------- 1 | GET {{base_url}}/api/numbers 2 | 3 | ### 4 | -------------------------------------------------------------------------------- /config/packages/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | channels: ['funding'] 3 | -------------------------------------------------------------------------------- /config/packages/test/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | strict_variables: true 3 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | /var 2 | /vendor 3 | /.env.local 4 | /.env.*.local 5 | /dumps 6 | -------------------------------------------------------------------------------- /config/packages/assets.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | assets: 3 | base_path: 'api' 4 | -------------------------------------------------------------------------------- /config/packages/beta/sentry.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: '../prod/sentry.*' } 3 | -------------------------------------------------------------------------------- /config/packages/beta/doctrine.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: '../prod/doctrine.*' } 3 | -------------------------------------------------------------------------------- /config/packages/beta/routing.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: '../prod/routing.*' } 3 | -------------------------------------------------------------------------------- /config/packages/dev/debug.yaml: -------------------------------------------------------------------------------- 1 | debug: 2 | dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" 3 | -------------------------------------------------------------------------------- /config/packages/dev/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /config/packages/prod/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: null 4 | -------------------------------------------------------------------------------- /config/packages/test/debug.yaml: -------------------------------------------------------------------------------- 1 | debug: 2 | dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" 3 | -------------------------------------------------------------------------------- /config/packages/test/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /config/packages/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | email_validation_mode: html5 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SC-Fleet-Manager/fleet-manager-api/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/Domain/OrgaId.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BO 6 | 7 | 8 | {% block body %} 9 | {% endblock %} 10 | 11 | {% block javascripts %} 12 | {% endblock %} 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Application/Home/Output/NumbersOutput.php: -------------------------------------------------------------------------------- 1 | bootEnv(dirname(__DIR__).'/.env'); 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = LF 5 | charset = utf-8 6 | indent_style = space 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [Makefile] 11 | indent_style = tab 12 | indent_size = 8 13 | 14 | [{*.php, *.yml, *.yaml, *.feature, *.yml.dist, *.yaml.dist}] 15 | indent_size = 4 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /src/Message/Funding/SendOrderRefundMail.php: -------------------------------------------------------------------------------- 1 | fundingId; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/preload.php: -------------------------------------------------------------------------------- 1 | fundingId; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docker/supervisor/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | logfile=/var/log/supervisor/supervisord.log 3 | pidfile=/var/run/supervisord.pid 4 | nodaemon=true 5 | user=root 6 | 7 | [rpcinterface:supervisor] 8 | supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface 9 | 10 | [supervisorctl] 11 | serverurl=unix:///var/run/supervisor.sock 12 | 13 | [include] 14 | files=/etc/supervisor/conf.d/*.conf 15 | -------------------------------------------------------------------------------- /src/Application/Provider/MemberProfileProviderInterface.php: -------------------------------------------------------------------------------- 1 | version; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Application/PatchNote/Output/LastPatchNotesOutput.php: -------------------------------------------------------------------------------- 1 | patchNotes, LastPatchNoteOutput::class); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /config/packages/test/framework.php: -------------------------------------------------------------------------------- 1 | loadFromExtension('framework', [ 4 | 'test' => true, 5 | 'session' => [ 6 | 'storage_id' => isset($_SERVER['TESTING_WITH_SESSION']) ? 'session.storage.native' : 'session.storage.mock_file', 7 | 'handler_id' => 'session.handler.native_file', 8 | ], 9 | 'cache' => [ 10 | 'app' => 'cache.adapter.apcu', 11 | 'pools' => [], 12 | ], 13 | ]); 14 | -------------------------------------------------------------------------------- /src/Form/Dto/PatchNote.php: -------------------------------------------------------------------------------- 1 | 'test_acceptance']); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docker/supervisor/app.prod.conf: -------------------------------------------------------------------------------- 1 | [program:organizations_sub_consumers] 2 | directory=/app 3 | command=php bin/console messenger:consume organizations_sub --limit=300 --time-limit=3600 4 | user=www-data 5 | stopsignal=TERM 6 | autostart=true 7 | startsecs=0 8 | autorestart=true 9 | numprocs=1 10 | process_name=%(program_name)s_%(process_num)02d 11 | stdout_logfile=/dev/fd/1 12 | stdout_logfile_maxbytes=0 13 | stderr_logfile=/dev/fd/2 14 | stderr_logfile_maxbytes=0 15 | -------------------------------------------------------------------------------- /src/Event/FundingUpdatedEvent.php: -------------------------------------------------------------------------------- 1 | funding = $funding; 15 | } 16 | 17 | public function getFunding(): Funding 18 | { 19 | return $this->funding; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /bin/phpunit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | paypalError = $paypalError; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Form/Dto/FundingPayment.php: -------------------------------------------------------------------------------- 1 | now = $now; 14 | } 15 | 16 | public function now(): \DateTimeInterface 17 | { 18 | return new \DateTimeImmutable($this->now); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Form/Dto/MonthlyCostCoverage.php: -------------------------------------------------------------------------------- 1 | profile = $profile; 14 | } 15 | 16 | public function getUserProfileByA0UID(string $jwt): ?array 17 | { 18 | return $this->profile; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Domain/Event/DeletedUserEvent.php: -------------------------------------------------------------------------------- 1 | userId; 18 | } 19 | 20 | public function getAuth0Username(): string 21 | { 22 | return $this->auth0Username; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Application/Repository/PatchNoteRepositoryInterface.php: -------------------------------------------------------------------------------- 1 | getModel(), 20 | $ship->getImageUrl(), 21 | $ship->getQuantity(), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ###> symfony/framework-bundle ### 2 | /.env.local 3 | /.env.local.php 4 | /.env.*.local 5 | /config/secrets/prod/prod.decrypt.private.php 6 | /public/bundles/ 7 | /var/ 8 | /vendor/ 9 | ###< symfony/framework-bundle ### 10 | ###> symfony/phpunit-bridge ### 11 | .phpunit 12 | .phpunit.result.cache 13 | /phpunit.xml 14 | ###< symfony/phpunit-bridge ### 15 | 16 | /docker-compose.override.yml 17 | /dumps/* 18 | 19 | ###> friendsofphp/php-cs-fixer ### 20 | /.php_cs 21 | /.php_cs.cache 22 | ###< friendsofphp/php-cs-fixer ### 23 | 24 | /http/http-client.env.json 25 | -------------------------------------------------------------------------------- /docker/php/php-cli.prod.ini: -------------------------------------------------------------------------------- 1 | expose_php = Off 2 | 3 | max_execution_time = -1 4 | max_input_time = -1 5 | memory_limit = 512M 6 | error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT 7 | display_errors = Off 8 | display_startup_errors = Off 9 | 10 | date.timezone = UTC 11 | 12 | opcache.enable=1 13 | opcache.enable_cli=1 14 | opcache.file_cache="/tmp" 15 | opcache.file_cache_only=1 16 | opcache.file_cache_consistency_checks=1 17 | 18 | realpath_cache_size = 4096K 19 | realpath_cache_ttl = 600 20 | 21 | ;error_log = /var/log/php/error-cli.log 22 | error_log = /proc/self/fd/2 23 | -------------------------------------------------------------------------------- /src/Application/Repository/ShipTemplateRepositoryInterface.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE users ADD handle VARCHAR(31) DEFAULT NULL'); 13 | } 14 | 15 | public function down(Schema $schema): void 16 | { 17 | $this->addSql('CREATE SCHEMA public'); 18 | $this->addSql('ALTER TABLE users DROP handle'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Domain/MemberProfile.php: -------------------------------------------------------------------------------- 1 | id; 17 | } 18 | 19 | public function getNickname(): ?string 20 | { 21 | return $this->nickname; 22 | } 23 | 24 | public function getHandle(): ?string 25 | { 26 | return $this->handle; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /migrations/Version20210407181655.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE users ADD nickname VARCHAR(31) DEFAULT NULL'); 13 | } 14 | 15 | public function down(Schema $schema): void 16 | { 17 | $this->addSql('CREATE SCHEMA public'); 18 | $this->addSql('ALTER TABLE users DROP nickname'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Entity/ShipChassis.php: -------------------------------------------------------------------------------- 1 | name = $name; 22 | } 23 | 24 | public function getName(): string 25 | { 26 | return $this->name; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /migrations/Version20210502100109.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE ships ALTER model TYPE VARCHAR(60)'); 13 | } 14 | 15 | public function down(Schema $schema): void 16 | { 17 | $this->addSql('CREATE SCHEMA public'); 18 | $this->addSql('ALTER TABLE ships ALTER model TYPE VARCHAR(32)'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Application/ShipTemplate/Output/CargoCapacityOutput.php: -------------------------------------------------------------------------------- 1 | getCapacity(), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Domain/UserFleet.php: -------------------------------------------------------------------------------- 1 | userId = $userId; 14 | $this->ships = $ships; 15 | } 16 | 17 | public function getUserId(): UserId 18 | { 19 | return $this->userId; 20 | } 21 | 22 | /** 23 | * @return UserShip[] 24 | */ 25 | public function getShips(): array 26 | { 27 | return $this->ships; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Entity/CargoCapacity.php: -------------------------------------------------------------------------------- 1 | capacity = $capacity; 22 | } 23 | 24 | public function getCapacity(): int 25 | { 26 | return $this->capacity; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /migrations/Version20210505165223.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE organization_ships ALTER model TYPE VARCHAR(60)'); 13 | } 14 | 15 | public function down(Schema $schema): void 16 | { 17 | $this->addSql('CREATE SCHEMA public'); 18 | $this->addSql('ALTER TABLE organization_ships ALTER model TYPE VARCHAR(32)'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Application/ShipTemplate/Output/ShipChassisOutput.php: -------------------------------------------------------------------------------- 1 | getName(), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Domain/UserProfile.php: -------------------------------------------------------------------------------- 1 | nickname; 17 | } 18 | 19 | public function getEmail(): ?string 20 | { 21 | return $this->email; 22 | } 23 | 24 | public function getDiscordId(): ?string 25 | { 26 | return $this->discordId; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | bootEnv(dirname(__DIR__).'/.env'); 11 | 12 | if ($_SERVER['APP_DEBUG']) { 13 | umask(0000); 14 | 15 | Debug::enable(); 16 | } 17 | 18 | $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); 19 | $request = Request::createFromGlobals(); 20 | $response = $kernel->handle($request); 21 | $response->send(); 22 | $kernel->terminate($request, $response); 23 | -------------------------------------------------------------------------------- /config/packages/test/jwt_auth.yaml: -------------------------------------------------------------------------------- 1 | jwt_auth: 2 | domain: "%env(AUTH0_DOMAIN)%" 3 | client_id: "%env(AUTH0_CLIENT_ID)%" 4 | audience: "%env(AUTH0_API_AUDIENCE)%" 5 | 6 | algorithm: "HS256" 7 | client_secret: "%env(AUTH0_CLIENT_SECRET)%" 8 | 9 | cache: "cache.app" 10 | 11 | validations: 12 | # Validate AUD claim against a value, such as an API identifier. Set to false to skip. Defaults to jwt_auth.audience. 13 | aud: "%env(AUTH0_API_AUDIENCE)%" 14 | # Validate the AZP claim against a value, such as a client ID. Set to false to skip. Defaults to false. 15 | azp: "%env(AUTH0_CLIENT_ID)%" 16 | -------------------------------------------------------------------------------- /src/Infrastructure/Repository/User/FakeAuth0Repository.php: -------------------------------------------------------------------------------- 1 | deletedUsers[] = $auth0Username; 15 | } 16 | 17 | /** 18 | * @return string[] 19 | */ 20 | public function getDeletedUsers(): array 21 | { 22 | return $this->deletedUsers; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /config/packages/prod/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | orm: 3 | auto_generate_proxy_classes: false 4 | metadata_cache_driver: 5 | type: pool 6 | pool: doctrine.system_cache_pool 7 | query_cache_driver: 8 | type: pool 9 | pool: doctrine.system_cache_pool 10 | result_cache_driver: 11 | type: pool 12 | pool: doctrine.result_cache_pool 13 | 14 | framework: 15 | cache: 16 | pools: 17 | doctrine.result_cache_pool: 18 | adapter: cache.app 19 | doctrine.system_cache_pool: 20 | adapter: cache.system 21 | -------------------------------------------------------------------------------- /config/packages/doctrine.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | env(DATABASE_URL): 'postgresql://localhost:5432/postgres?serverVersion=13&charset=utf8' 3 | 4 | doctrine: 5 | dbal: 6 | url: '%env(resolve:DATABASE_URL)%' 7 | override_url: true 8 | orm: 9 | auto_generate_proxy_classes: true 10 | naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware 11 | auto_mapping: true 12 | mappings: 13 | App: 14 | is_bundle: false 15 | type: annotation 16 | dir: '%kernel.project_dir%/src/Entity' 17 | prefix: 'App\Entity' 18 | alias: App 19 | -------------------------------------------------------------------------------- /src/Application/MyOrganizations/Output/OrganizationsCollectionOutput.php: -------------------------------------------------------------------------------- 1 | role = $role; 24 | } 25 | 26 | public function getRole(): ?string 27 | { 28 | return $this->role; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /templates/emails/order_capture_summary.txt.twig: -------------------------------------------------------------------------------- 1 | Funding properties : 2 | 3 | Amount : {{ (funding.amount / 100)|number_format(2) }} {{ funding.currency }} 4 | Net Amount : {{ (funding.netAmount / 100)|number_format(2) }} {{ funding.currency }} 5 | ID : {{ funding.id }} 6 | Payer ID : {{ funding.user.id }} 7 | Payer nickname : {{ funding.user.nickname }} 8 | Payer email : {{ funding.user.email }} 9 | Gateway : {{ funding.gateway }} 10 | Created at : {{ funding.createdAt|date('r') }} 11 | Paypal Order Id : {{ funding.paypalOrderId }} 12 | Paypal Status : {{ funding.paypalStatus }} 13 | Paypal Purchase : 14 | {{ funding.paypalPurchase|json_encode(constant('JSON_PRETTY_PRINT')) }} 15 | -------------------------------------------------------------------------------- /migrations/Version20210523102656.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE ships ADD template_id UUID DEFAULT NULL'); 13 | $this->addSql('COMMENT ON COLUMN ships.template_id IS \'(DC2Type:ulid)\''); 14 | } 15 | 16 | public function down(Schema $schema): void 17 | { 18 | $this->addSql('CREATE SCHEMA public'); 19 | $this->addSql('ALTER TABLE ships DROP template_id'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Application/Profile/DeleteAccountHandler.php: -------------------------------------------------------------------------------- 1 | getAuth0Username(); 19 | 20 | $this->auth0Repository->delete($username); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Domain/Exception/DomainException.php: -------------------------------------------------------------------------------- 1 | error = $error; 19 | $this->userMessage = $userMessage; 20 | $this->context = $context; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /config/packages/test/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: fingers_crossed 5 | action_level: error 6 | handler: nested 7 | excluded_http_codes: [404, 405] 8 | buffer_size: 50 # How many messages should be saved? Prevent memory leaks 9 | nested: 10 | type: stream 11 | path: "%kernel.logs_dir%/%kernel.environment%-error.log" 12 | level: info 13 | #################### 14 | streamed_main: 15 | type: stream 16 | path: "%kernel.logs_dir%/%kernel.environment%.log" 17 | level: debug 18 | channels: [ "!event" ] 19 | -------------------------------------------------------------------------------- /src/Application/Repository/FleetRepositoryInterface.php: -------------------------------------------------------------------------------- 1 | 'forbidden', 17 | ], 403); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /config/packages/prod/sentry.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | env(SENTRY_DSN): '' 3 | 4 | sentry: 5 | dsn: '%env(SENTRY_DSN)%' 6 | options: 7 | integrations: 8 | - 'Sentry\Integration\IgnoreErrorsIntegration' 9 | 10 | services: 11 | Sentry\Integration\IgnoreErrorsIntegration: 12 | arguments: 13 | $options: 14 | ignore_exceptions: 15 | - 'Symfony\Component\Security\Core\Exception\AccessDeniedException' 16 | - 'Symfony\Component\Security\Core\Exception\AuthenticationException' 17 | - 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException' 18 | - 'App\Domain\Exception\DomainException' 19 | -------------------------------------------------------------------------------- /src/Application/Repository/UserRepositoryInterface.php: -------------------------------------------------------------------------------- 1 | getShips() as $ship) { 21 | $ships[] = OrganizationsItemFleetShipsOutput::createFromShip($ship); 22 | } 23 | 24 | return new self($ships); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Service/Funding/PaypalCheckoutInterface.php: -------------------------------------------------------------------------------- 1 | addSql('DROP INDEX fleetid_name_idx'); 13 | $this->addSql('ALTER TABLE ships RENAME COLUMN name TO model'); 14 | } 15 | 16 | public function down(Schema $schema): void 17 | { 18 | $this->addSql('CREATE SCHEMA public'); 19 | $this->addSql('ALTER TABLE ships RENAME COLUMN model TO name'); 20 | $this->addSql('CREATE UNIQUE INDEX fleetid_name_idx ON ships (fleet_id, name)'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Application/ShipTemplate/Output/CrewOutput.php: -------------------------------------------------------------------------------- 1 | getMin(), 26 | $crew->getMax(), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Infrastructure/Provider/Organizations/InMemoryUserFleetProvider.php: -------------------------------------------------------------------------------- 1 | userFleet = $userFleet; 17 | } 18 | 19 | public function getUserFleet(MemberId $memberId): UserFleet 20 | { 21 | return $this->userFleet ?? new UserFleet(UserId::fromString((string) $memberId), []); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Integration/KernelTestCase.php: -------------------------------------------------------------------------------- 1 | get('doctrine.dbal.default_connection'); 16 | static::$connection->beginTransaction(); 17 | } 18 | 19 | protected function tearDown(): void 20 | { 21 | if (static::$connection->isTransactionActive()) { 22 | static::$connection->rollBack(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Application/MyOrganizations/Output/OrganizationMembersItemOutput.php: -------------------------------------------------------------------------------- 1 | userRepository->getById($userId); 21 | if ($user === null) { 22 | throw new NotFoundUserException($userId); 23 | } 24 | 25 | $user->changeHandle($handle); 26 | 27 | $this->userRepository->save($user); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Application/Profile/ChangeNicknameService.php: -------------------------------------------------------------------------------- 1 | userRepository->getById($userId); 19 | if ($user === null) { 20 | throw new NotFoundUserException($userId); 21 | } 22 | 23 | $user->changeNickname($nickname); 24 | 25 | $this->userRepository->save($user); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Domain/Event/UpdatedFleetEvent.php: -------------------------------------------------------------------------------- 1 | getShips() as $ship) { 22 | $ships[] = UpdatedShip::createFromShip($ship); 23 | } 24 | 25 | return new self( 26 | $fleet->getUserId(), 27 | $ships, 28 | $fleet->getVersion(), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | services: 3 | php: 4 | image: 186694923554.dkr.ecr.eu-west-1.amazonaws.com/fleet-manager-api-php:test-latest 5 | hostname: php 6 | depends_on: 7 | - redis 8 | supervisor: 9 | image: 186694923554.dkr.ecr.eu-west-1.amazonaws.com/fleet-manager-api-supervisor:test-latest 10 | hostname: supervisor 11 | depends_on: 12 | - redis 13 | apache: 14 | image: 186694923554.dkr.ecr.eu-west-1.amazonaws.com/fleet-manager-api-apache:test-latest 15 | hostname: apache 16 | environment: 17 | PHP_HANDLER_HOST: php:9000 18 | depends_on: 19 | - php 20 | redis: 21 | image: bitnami/redis:5.0.9-debian-10-r6 22 | hostname: redis 23 | -------------------------------------------------------------------------------- /src/Infrastructure/Provider/MyFleet/InMemoryListTemplatesProvider.php: -------------------------------------------------------------------------------- 1 | templateOfUser = $template; 17 | } 18 | 19 | public function getShipTemplateOfUser(ShipTemplateId $templateId, UserId $userId): ?UserShipTemplate 20 | { 21 | return $this->templateOfUser; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Infrastructure/Validator/UniqueOrganizationSid.php: -------------------------------------------------------------------------------- 1 | excludeOrgaId = $excludeOrgaId; 21 | $this->message = $message ?? 'This SID is already taken.'; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Application/Profile/SavePreferencesService.php: -------------------------------------------------------------------------------- 1 | userRepository->getById($userId); 19 | if ($user === null) { 20 | throw new NotFoundUserException($userId); 21 | } 22 | 23 | $user->setSupporterVisible($supporterVisible); 24 | 25 | $this->userRepository->save($user); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Star Citizen Fleet Manager # 2 | 3 | ## Installation for development ## 4 | 5 | **Requirements** 6 | 7 | - git 8 | - docker 9 | - docker-compose 10 | 11 | **Clone repository** 12 | 13 | ``` 14 | git clone https://github.com/Ioni14/starcitizen-fleet-manager.git 15 | cd starcitizen-fleet-manager 16 | ``` 17 | 18 | **Customize environment variables** 19 | 20 | ``` 21 | echo "APP_ENV=dev" > .env.local 22 | ``` 23 | 24 | **Customize docker-compose.override.yml** 25 | 26 | cp docker-compose.override.yml.dist docker-compose.override.yml 27 | 28 | Customize the ports according to your needs, configure your dev reverse-proxy, etc. 29 | 30 | **Launch the stack (build & up containers)** 31 | 32 | ``` 33 | make install 34 | ``` 35 | 36 | **Launch all tests** 37 | ``` 38 | make -j8 tests 39 | ``` 40 | -------------------------------------------------------------------------------- /src/Infrastructure/Controller/ShipTemplate/Input/CreateTemplateCrewInput.php: -------------------------------------------------------------------------------- 1 | getUserId(); 19 | 20 | $fleet = $this->fleetRepository->getFleetByUser($userId); 21 | if ($fleet === null) { 22 | return; 23 | } 24 | 25 | $this->fleetRepository->delete($fleet); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Domain/MyFleet/UserShipTemplate.php: -------------------------------------------------------------------------------- 1 | id = $id; 16 | $this->model = $model; 17 | $this->pictureUrl = $pictureUrl; 18 | } 19 | 20 | public function getId(): ShipTemplateId 21 | { 22 | return $this->id; 23 | } 24 | 25 | public function getModel(): string 26 | { 27 | return $this->model; 28 | } 29 | 30 | public function getPictureUrl(): ?string 31 | { 32 | return $this->pictureUrl; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Domain/Exception/NotFoundOrganizationException.php: -------------------------------------------------------------------------------- 1 | entityClass); 23 | Assert::propertyExists($this->entityClass, $field); 24 | $this->message = $message ?: 'This email is taken. Please choose another.'; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Infrastructure/Validator/CountShipsLessThan.php: -------------------------------------------------------------------------------- 1 | message = $message ?? "You have reached the limit of $max ships."; 19 | $this->max = $max; 20 | } 21 | 22 | public function getTargets(): string 23 | { 24 | return self::CLASS_CONSTRAINT; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Domain/EntityId.php: -------------------------------------------------------------------------------- 1 | ulid; 24 | } 25 | 26 | public function equals(EntityId $other): bool 27 | { 28 | return $this->ulid->equals($other->ulid); 29 | } 30 | 31 | public function __toString(): string 32 | { 33 | return $this->ulid->toRfc4122(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Domain/Exception/NotFoundFleetByUserException.php: -------------------------------------------------------------------------------- 1 | getName(), 26 | $manufacturer->getCode(), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Domain/Exception/ConflictVersionException.php: -------------------------------------------------------------------------------- 1 | getMessage() ?? ''), 24 | $code, 25 | $previous, 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Domain/Exception/FailedValidationException.php: -------------------------------------------------------------------------------- 1 | userRepository->getById($userId); 21 | if ($user === null) { 22 | return; 23 | } 24 | 25 | $this->userRepository->delete($user); 26 | $this->bus->dispatch(new DeletedUserEvent($userId, $user->getAuth0Username())); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Domain/Exception/NotFoundOrganizationFleetException.php: -------------------------------------------------------------------------------- 1 | message = $message ?? "You have reached the limit of $max organizations created."; 19 | $this->max = $max; 20 | } 21 | 22 | public function getTargets(): string 23 | { 24 | return self::CLASS_CONSTRAINT; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Application/MyOrganizations/Output/OrganizationShipOwnersItemOutput.php: -------------------------------------------------------------------------------- 1 | json([ 23 | 'currency' => $this->currency, 24 | 'paypalClientId' => $this->paypalClientId, 25 | ]); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /config/packages/security.yaml: -------------------------------------------------------------------------------- 1 | security: 2 | # enable_authenticator_manager: true 3 | providers: 4 | user_entity_jwt: 5 | id: 'App\Security\UserEntityJwtProvider' 6 | firewalls: 7 | dev: 8 | pattern: ^/(_(profiler|wdt)|favicon\.ico)/ 9 | security: false 10 | main: 11 | provider: user_entity_jwt 12 | stateless: true 13 | anonymous: true 14 | guard: 15 | authenticators: 16 | - jwt_auth.security.guard.jwt_guard_authenticator 17 | logout: 18 | path: logout 19 | access_denied_handler: App\Security\ApiAccessDeniedHandler 20 | 21 | role_hierarchy: 22 | ROLE_ADMIN: ROLE_USER 23 | 24 | access_control: 25 | - { path: ^/bo/, roles: ROLE_ADMIN } 26 | - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY } 27 | -------------------------------------------------------------------------------- /src/Application/MyFleet/ClearMyFleetService.php: -------------------------------------------------------------------------------- 1 | fleetRepository->getFleetByUser($userId); 23 | if ($fleet === null) { 24 | return; 25 | } 26 | 27 | $fleet->deleteAllShips($this->clock->now()); 28 | 29 | $this->fleetRepository->save($fleet); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Infrastructure/Repository/User/Auth0Repository.php: -------------------------------------------------------------------------------- 1 | managementApi->users()->delete($auth0Username); 23 | } catch (\Throwable $e) { 24 | $this->logger->error('Unable to delete user on Auth0 : '.$e->getMessage(), ['exception' => $e, 'username' => $auth0Username]); 25 | 26 | throw $e; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /fixtures/patch_note.yaml: -------------------------------------------------------------------------------- 1 | App\Entity\PatchNote: 2 | patch_note_1: 3 | __construct: 4 | - '<(new \App\Domain\PatchNoteId(\Symfony\Component\Uid\Ulid::fromString("01F17Y39FNFNJG7ECMM4YG9SBE")))>' 5 | - 'My new patch 1' 6 | - | 7 | Hello everyone, 8 | Here is my new patch 1 9 | Goodbye! 10 | - 'https://blog.fleet-manager.space/my-patch-1' 11 | - '<(new \DateTimeImmutable("2019-04-03 11:22:33"))>' 12 | patch_note_2: 13 | __construct: 14 | - '<(new \App\Domain\PatchNoteId(\Symfony\Component\Uid\Ulid::fromString("01F17Y4GWNQ89CNPM6T6M1R894")))>' 15 | - 'My new patch 2' 16 | - | 17 | Hello everyone, 18 | Here is my new patch 2 19 | Goodbye! 20 | - null 21 | - '<(new \DateTimeImmutable("2019-04-04 11:22:33"))>' 22 | -------------------------------------------------------------------------------- /src/Domain/UserShip.php: -------------------------------------------------------------------------------- 1 | shipId = $shipId; 15 | $this->model = $model; 16 | $this->imageUrl = $imageUrl; 17 | $this->quantity = $quantity; 18 | } 19 | 20 | public function getShipId(): ShipId 21 | { 22 | return $this->shipId; 23 | } 24 | 25 | public function getModel(): string 26 | { 27 | return $this->model; 28 | } 29 | 30 | public function getImageUrl(): ?string 31 | { 32 | return $this->imageUrl; 33 | } 34 | 35 | public function getQuantity(): int 36 | { 37 | return $this->quantity; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Application/Home/NumbersService.php: -------------------------------------------------------------------------------- 1 | userRepository->countUsers(), 23 | $this->organizationFleetRepository->countFleets(), 24 | $this->fleetRepository->countShips(), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Application/ShipTemplate/Output/PriceOutput.php: -------------------------------------------------------------------------------- 1 | getPledge() !== null ? $price->getPledge()->getAmount() : null, 26 | $price->getIngame() !== null ? $price->getIngame()->getAmount() : null, 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Infrastructure/Controller/MyFleet/Input/CreateShipFromTemplateInput.php: -------------------------------------------------------------------------------- 1 | 5 |

Patch Note Create

6 | 7 |

Back to the list

8 | 9 |
10 | {{ form_row(form.title) }} 11 | {{ form_row(form.body, {'attr': {'rows': '10'}}) }} 12 | {{ form_row(form.link, {'attr': {'placeholder': 'https://blog.fleet-manager.space/'}}) }} 13 | 14 | 15 | {% if form._token is defined %} 16 | {{ form_widget(form._token) }} 17 | {% endif %} 18 |
19 | 20 | {% endblock %} 21 | 22 | {% block javascripts %} 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /src/Application/MyFleet/CreateShipService.php: -------------------------------------------------------------------------------- 1 | fleetRepository->getFleetByUser($userId); 22 | if ($fleet === null) { 23 | $fleet = new Fleet($userId, $this->clock->now()); 24 | } 25 | 26 | $fleet->addShip($shipId, $model, $imageUrl, $quantity ?? 1, $this->clock->now()); 27 | 28 | $this->fleetRepository->save($fleet); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Infrastructure/Provider/Organizations/InMemoryMemberProfileProvider.php: -------------------------------------------------------------------------------- 1 | memberProfiles = []; 19 | foreach ($memberProfiles as $memberProfile) { 20 | $this->memberProfiles[(string) $memberProfile->getId()] = $memberProfile; 21 | } 22 | } 23 | 24 | /** 25 | * {@inheritDoc} 26 | */ 27 | public function getProfiles(array $memberIds): array 28 | { 29 | return $this->memberProfiles; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Service/PayPal/MockVerifyWebhookSignatureFactory.php: -------------------------------------------------------------------------------- 1 | verifyWebhookSignature = new VerifyWebhookSignature(); 17 | } 18 | 19 | public function setVerifyWebhookSignature(VerifyWebhookSignature $verifyWebhookSignature): void 20 | { 21 | $this->verifyWebhookSignature = $verifyWebhookSignature; 22 | } 23 | 24 | public function createVerifyWebhookSignature(Request $request): VerifyWebhookSignature 25 | { 26 | return $this->verifyWebhookSignature; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Domain/Exception/NotFoundShipException.php: -------------------------------------------------------------------------------- 1 | fieldHandle = $fieldHandle; 19 | $this->fieldExcludeUserId = $fieldExcludeUserId; 20 | $this->message = $message ?? 'This handle is already taken.'; 21 | } 22 | 23 | public function getTargets(): string 24 | { 25 | return Constraint::CLASS_CONSTRAINT; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /templates/back_office/patch_note/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'back_office_layout.html.twig' %} 2 | 3 | {% block body %} 4 |
5 |

Patch Note Edit

6 | 7 |

Back to the list

8 | 9 |
10 | {{ form_row(form.title) }} 11 | {{ form_row(form.body, {'attr': {'rows': '10'}}) }} 12 | {{ form_row(form.link, {'attr': {'placeholder': 'https://blog.fleet-manager.space/'}}) }} 13 | 14 | 15 | {% if form._token is defined %} 16 | {{ form_widget(form._token) }} 17 | {% endif %} 18 |
19 |
20 | {% endblock %} 21 | 22 | {% block javascripts %} 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /src/Application/Profile/PublicProfilesService.php: -------------------------------------------------------------------------------- 1 | userRepository->getByIds($userIds); 24 | 25 | $result = []; 26 | foreach ($users as $user) { 27 | $result[] = new PublicProfileOutput( 28 | $user->getId(), 29 | $user->getNickname(), 30 | $user->getHandle(), 31 | ); 32 | } 33 | 34 | return $result; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Application/MyOrganizations/Output/OrganizationsItemOutput.php: -------------------------------------------------------------------------------- 1 | pictureUrl ?? 'http', 'http'); 28 | Assert::lengthBetween($this->model, 2, 60); 29 | Assert::maxLength($this->pictureUrl, 1023); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Controller/Funding/MyBackingsController.php: -------------------------------------------------------------------------------- 1 | denyAccessUnlessGranted('ROLE_USER'); 22 | 23 | /** @var User $user */ 24 | $user = $this->getUser(); 25 | 26 | $fundings = $this->fundingRepository->findBy(['user' => $user]); 27 | 28 | return $this->json($fundings, 200, [], ['groups' => 'my_backings']); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Domain/Exception/NotFounderOfOrganizationException.php: -------------------------------------------------------------------------------- 1 | getThrowable(); 20 | if (!$e instanceof DomainException) { 21 | return; 22 | } 23 | 24 | $data = array_merge([ 25 | 'error' => $e->error, 26 | 'errorMessage' => $e->userMessage, 27 | ], $e->context); 28 | 29 | $event->setResponse( 30 | new JsonResponse($this->serializer->serialize($data, 'json'), 400, [], true) 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Validator/Constraints/UniqueFieldValidator.php: -------------------------------------------------------------------------------- 1 | entityManager->getRepository($constraint->entityClass); 22 | $foundEntity = $repo->findOneBy([$constraint->field => $value]); 23 | if ($foundEntity === null) { 24 | return; 25 | } 26 | 27 | $this->context->buildViolation($constraint->message)->addViolation(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docker/php/www.dev.conf: -------------------------------------------------------------------------------- 1 | [www] 2 | user = www-data 3 | group = www-data 4 | listen = 127.0.0.1:9000 5 | 6 | ; https://symfony.com/blog/logging-in-symfony-and-the-cloud 7 | catch_workers_output = yes 8 | decorate_workers_output = no 9 | access.log = /proc/self/fd/2 10 | access.format='{"time_local":"%{%Y-%m-%dT%H:%M:%S%z}T","client_ip":"%{HTTP_X_FORWARDED_FOR}e","remote_addr":"%R","remote_user":"%u","request":"%m %{REQUEST_URI}e %{SERVER_PROTOCOL}e","status":"%s","body_bytes_sent":"%l","request_time":"%d","http_referrer":"%{HTTP_REFERER}e","http_user_agent":"%{HTTP_USER_AGENT}e","request_id":"%{HTTP_X_REQUEST_ID}e"}' 11 | ; slowlog = /app/var/log/php/$pool.slow.log 12 | ; request_slowlog_timeout = 3s 13 | request_terminate_timeout = 120s 14 | 15 | pm = dynamic 16 | pm.max_children = 8 17 | pm.start_servers = 3 18 | pm.min_spare_servers = 2 19 | pm.max_spare_servers = 4 20 | pm.max_requests = 300 21 | 22 | ; Web-UI 23 | ; pm.status_path = /status 24 | ; Healthcheck 25 | ; ping.path = /ping 26 | 27 | security.limit_extensions = .php 28 | -------------------------------------------------------------------------------- /docker/php/www.prod.conf: -------------------------------------------------------------------------------- 1 | [www] 2 | user = www-data 3 | group = www-data 4 | listen = 127.0.0.1:9000 5 | 6 | ; https://symfony.com/blog/logging-in-symfony-and-the-cloud 7 | catch_workers_output = yes 8 | decorate_workers_output = no 9 | access.log = /proc/self/fd/2 10 | access.format='{"time_local":"%{%Y-%m-%dT%H:%M:%S%z}T","client_ip":"%{HTTP_X_FORWARDED_FOR}e","remote_addr":"%R","remote_user":"%u","request":"%m %{REQUEST_URI}e %{SERVER_PROTOCOL}e","status":"%s","body_bytes_sent":"%l","request_time":"%d","http_referrer":"%{HTTP_REFERER}e","http_user_agent":"%{HTTP_USER_AGENT}e","request_id":"%{HTTP_X_REQUEST_ID}e"}' 11 | ; slowlog = /app/var/log/php/$pool.slow.log 12 | ; request_slowlog_timeout = 3s 13 | request_terminate_timeout = 120s 14 | 15 | pm = dynamic 16 | pm.max_children = 8 17 | pm.start_servers = 3 18 | pm.min_spare_servers = 2 19 | pm.max_spare_servers = 4 20 | pm.max_requests = 300 21 | 22 | ; Web-UI 23 | ; pm.status_path = /status 24 | ; Healthcheck 25 | ; ping.path = /ping 26 | 27 | security.limit_extensions = .php 28 | -------------------------------------------------------------------------------- /src/Application/Profile/ProfileService.php: -------------------------------------------------------------------------------- 1 | userRepository->getById($userId); 20 | if ($user === null) { 21 | throw new NotFoundUserException($userId); 22 | } 23 | 24 | return new ProfileOutput( 25 | $userId, 26 | $user->getAuth0Username(), 27 | $user->getNickname(), 28 | $user->getHandle(), 29 | $user->isSupporterVisible(), 30 | $user->getCoins(), 31 | $user->getCreatedAt(), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /config/packages/psr_http_message_bridge.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | _defaults: 3 | autowire: true 4 | autoconfigure: true 5 | 6 | Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface: 7 | '@Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory' 8 | 9 | Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface: 10 | '@Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory' 11 | 12 | Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory: null 13 | Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory: null 14 | 15 | # Uncomment the following line to allow controllers to receive a 16 | # PSR-7 server request object instead of an HttpFoundation request 17 | #Symfony\Bridge\PsrHttpMessage\ArgumentValueResolver\PsrServerRequestResolver: null 18 | 19 | # Uncomment the following line to allow controllers to return a 20 | # PSR-7 response object instead of an HttpFoundation response 21 | #Symfony\Bridge\PsrHttpMessage\EventListener\PsrResponseListener: null 22 | -------------------------------------------------------------------------------- /src/Domain/Exception/FounderOfOrganizationException.php: -------------------------------------------------------------------------------- 1 | patchNoteRepository->findBy([], ['createdAt' => 'DESC']); 23 | 24 | return $this->render('back_office/patch_note/list.html.twig', [ 25 | 'patch_notes' => $patchNotes, 26 | ]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Entity/ShipSize.php: -------------------------------------------------------------------------------- 1 | size = $size; 22 | } 23 | 24 | public function getSize(): ?string 25 | { 26 | return $this->size; 27 | } 28 | 29 | public static function unknown(): self 30 | { 31 | return new self(null); 32 | } 33 | 34 | public static function __callStatic(string $name, array $arguments): self 35 | { 36 | if (!in_array($name, self::SIZES, true)) { 37 | throw new \RuntimeException(sprintf('static method %s does not exist.', $name)); 38 | } 39 | 40 | return new self($name); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Domain/Exception/NotJoinedOrganizationMemberException.php: -------------------------------------------------------------------------------- 1 | pledge = $pledge !== null ? $pledge->getAmount() : null; 26 | $this->ingame = $ingame !== null ? $ingame->getAmount() : null; 27 | } 28 | 29 | public function getPledge(): ?Money 30 | { 31 | return $this->pledge !== null ? Money::USD($this->pledge) : null; 32 | } 33 | 34 | public function getIngame(): ?Money 35 | { 36 | return $this->ingame !== null ? Money::UEC($this->ingame) : null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Domain/Exception/AlreadyMemberOfOrganizationException.php: -------------------------------------------------------------------------------- 1 | userRepository->getById($userId); 22 | if ($user === null) { 23 | throw new NotFoundUserException($userId); 24 | } 25 | 26 | $patchNoteId = $this->patchNoteRepository->getOneRecentPatchNoteId($user->getLastPatchNoteReadAt()); 27 | 28 | return new HasNewPatchNoteOutput($patchNoteId !== null); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Application/MyFleet/DeleteShipService.php: -------------------------------------------------------------------------------- 1 | fleetRepository->getFleetByUser($userId); 27 | if ($fleet === null) { 28 | throw new NotFoundFleetByUserException($userId); 29 | } 30 | 31 | $fleet->deleteShip($shipId, $this->clock->now()); 32 | 33 | $this->fleetRepository->save($fleet); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Domain/Exception/FullyJoinedMemberOfOrganizationException.php: -------------------------------------------------------------------------------- 1 | profilesService->handle($memberIds); 23 | 24 | $result = []; 25 | foreach ($profiles as $profile) { 26 | $result[(string) $profile->id] = new MemberProfile( 27 | MemberId::fromString((string) $profile->id), 28 | $profile->nickname, 29 | $profile->handle, 30 | ); 31 | } 32 | 33 | return $result; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /config/services_test.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | _defaults: 3 | autowire: true 4 | autoconfigure: true 5 | public: true 6 | 7 | App\Application\: 8 | resource: '../src/Application' 9 | exclude: 10 | - '../src/Application/**/*/Input' 11 | - '../src/Application/**/*/Output' 12 | - '../src/Application/Exception' 13 | - '../src/Infrastructure/**/*/Input' 14 | 15 | App\Tests\Service\PayPal\MockPayPalHttpClient: ~ 16 | App\Tests\Service\PayPal\MockVerifyWebhookSignatureFactory: ~ 17 | App\Service\Funding\PaypalCheckout: 18 | arguments: 19 | $client: '@App\Tests\Service\PayPal\MockPayPalHttpClient' 20 | $verifyWebhookSignatureFactory: '@App\Tests\Service\PayPal\MockVerifyWebhookSignatureFactory' 21 | 22 | App\Infrastructure\Security\FakeAuth0Service: 23 | parent: 'jwt_auth.auth0_service' 24 | Auth0\JWTAuthBundle\Security\Auth0Service: '@App\Infrastructure\Security\FakeAuth0Service' 25 | 26 | App\Application\Repository\Auth0RepositoryInterface: '@App\Infrastructure\Repository\User\FakeAuth0Repository' 27 | -------------------------------------------------------------------------------- /config/packages/dev/monolog.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | env(MONOLOG_MAILER_TO_0): 'root@localhost' 3 | 4 | monolog: 5 | handlers: 6 | main: 7 | type: stream 8 | path: "php://stderr" 9 | level: debug 10 | channels: [ "!event" ] 11 | console: 12 | type: console 13 | process_psr_3_messages: false 14 | channels: ["!event", "!doctrine", "!console"] 15 | ########################## 16 | fingers: 17 | type: fingers_crossed 18 | action_level: error 19 | handler: dedup_mail 20 | buffer_size: 50 21 | dedup_mail: 22 | type: deduplication 23 | time: 5 24 | store: '/tmp/monolog_handler_deduplicated' 25 | handler: mailer 26 | mailer: 27 | type: symfony_mailer 28 | from_email: 'noreply@fleet-manager.space' 29 | to_email: '%env(MONOLOG_MAILER_TO_0)%' 30 | subject: '[FM API %kernel.environment%] Error : %%message%%' 31 | formatter: monolog.formatter.html 32 | content_type: text/html 33 | -------------------------------------------------------------------------------- /migrations/Version20210413195714.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE TABLE memberships (member_id UUID NOT NULL, organization_id UUID NOT NULL, joined BOOLEAN DEFAULT \'false\' NOT NULL, PRIMARY KEY(member_id, organization_id))'); 13 | $this->addSql('CREATE INDEX IDX_865A477632C8A3DE ON memberships (organization_id)'); 14 | $this->addSql('COMMENT ON COLUMN memberships.member_id IS \'(DC2Type:ulid)\''); 15 | $this->addSql('COMMENT ON COLUMN memberships.organization_id IS \'(DC2Type:ulid)\''); 16 | $this->addSql('ALTER TABLE memberships ADD CONSTRAINT FK_865A477632C8A3DE FOREIGN KEY (organization_id) REFERENCES organizations (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); 17 | } 18 | 19 | public function down(Schema $schema): void 20 | { 21 | $this->addSql('CREATE SCHEMA public'); 22 | $this->addSql('DROP TABLE memberships'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Application/MyOrganizations/Output/OrganizationsItemFleetShipsOutput.php: -------------------------------------------------------------------------------- 1 | getModel(), 30 | $ship->getImageUrl(), 31 | $ship->getQuantity(), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Application/MyOrganizations/Output/MyOrganizationsItemOutput.php: -------------------------------------------------------------------------------- 1 | new feature.") 21 | */ 22 | public string $body, 23 | /** 24 | * @OpenApi\Property(type="string", format="url", nullable=true, example="https://blog.fleet-manager.space/new-feature-released") 25 | */ 26 | public ?string $link, 27 | /** 28 | * @OpenApi\Property(type="string", format="date-time") 29 | */ 30 | public \DateTimeInterface $createdAt, 31 | ) { 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /config/packages/nyholm_psr7.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # Register nyholm/psr7 services for autowiring with PSR-17 (HTTP factories) 3 | Psr\Http\Message\RequestFactoryInterface: '@nyholm.psr7.psr17_factory' 4 | Psr\Http\Message\ResponseFactoryInterface: '@nyholm.psr7.psr17_factory' 5 | Psr\Http\Message\ServerRequestFactoryInterface: '@nyholm.psr7.psr17_factory' 6 | Psr\Http\Message\StreamFactoryInterface: '@nyholm.psr7.psr17_factory' 7 | Psr\Http\Message\UploadedFileFactoryInterface: '@nyholm.psr7.psr17_factory' 8 | Psr\Http\Message\UriFactoryInterface: '@nyholm.psr7.psr17_factory' 9 | 10 | # Register nyholm/psr7 services for autowiring with HTTPlug factories 11 | Http\Message\MessageFactory: '@nyholm.psr7.httplug_factory' 12 | Http\Message\RequestFactory: '@nyholm.psr7.httplug_factory' 13 | Http\Message\ResponseFactory: '@nyholm.psr7.httplug_factory' 14 | Http\Message\StreamFactory: '@nyholm.psr7.httplug_factory' 15 | Http\Message\UriFactory: '@nyholm.psr7.httplug_factory' 16 | 17 | nyholm.psr7.psr17_factory: 18 | class: Nyholm\Psr7\Factory\Psr17Factory 19 | 20 | nyholm.psr7.httplug_factory: 21 | class: Nyholm\Psr7\Factory\HttplugFactory 22 | -------------------------------------------------------------------------------- /src/Domain/Notification/FeedbackNotification.php: -------------------------------------------------------------------------------- 1 | format('Y-m-d'); 21 | parent::__construct(<<setName('app:delete-old-created-funding'); 22 | } 23 | 24 | protected function execute(InputInterface $input, OutputInterface $output): int 25 | { 26 | $io = new SymfonyStyle($input, $output); 27 | 28 | $answer = $io->askQuestion(new ConfirmationQuestion('Would you really want to delete old created fundings?', false)); 29 | if ($answer) { 30 | $this->fundingRepository->deleteOldCreated(new \DateTimeImmutable('-1 week')); 31 | } 32 | 33 | return 0; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Application/MyFleet/Output/MyFleetShipOutput.php: -------------------------------------------------------------------------------- 1 | shipTemplateRepository->getTemplateById($templateId); 22 | if ($template !== null && $template->getAuthorId()->equals(TemplateAuthorId::fromString((string)$userId))) { 23 | return new UserShipTemplate( 24 | $template->getId(), 25 | $template->getModel(), 26 | $template->getPictureUrl(), 27 | ); 28 | } 29 | 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Listener/Funding/UpdateSupporterAdvantagesListener.php: -------------------------------------------------------------------------------- 1 | getFunding(); 21 | 22 | $user = $updatedFunding->getUser(); 23 | if ($user === null) { 24 | return; 25 | } 26 | 27 | // recompute all FM Coins balance 28 | /** @var Funding[] $fundings */ 29 | $fundings = $this->fundingRepository->findBy(['user' => $user, 'paypalStatus' => ['COMPLETED', 'PARTIALLY_REFUNDED']]); 30 | $balance = 0; 31 | foreach ($fundings as $funding) { 32 | $balance += $funding->getEffectiveAmount(); 33 | } 34 | $user->setCoins($balance); 35 | $this->entityManager->flush(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /config/packages/messenger.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | messenger: 3 | serializer: 4 | default_serializer: messenger.transport.symfony_serializer 5 | symfony_serializer: 6 | format: json 7 | context: { } 8 | default_bus: event.bus 9 | buses: 10 | event.bus: ~ 11 | # default_middleware: allow_no_handlers 12 | failure_transport: failed 13 | transports: 14 | failed: '%env(MESSENGER_TRANSPORT_FAILED_DSN)%' 15 | organizations_sub: 16 | dsn: '%env(MESSENGER_TRANSPORT_ORGANIZATIONS_SUB_DSN)%' 17 | retry_strategy: 18 | max_retries: 3 19 | delay: 1000 20 | multiplier: 2 21 | max_delay: 0 22 | my_fleet_internal: 'sync://' 23 | sync: 'sync://' 24 | 25 | routing: 26 | 'App\Message\Funding\SendOrderCaptureSummaryMail': sync 27 | 'App\Message\Funding\SendOrderRefundMail': sync 28 | 'App\Domain\Event\DeletedUserEvent': [ my_fleet_internal, organizations_sub ] 29 | 'App\Domain\Event\UpdatedFleetEvent': [ organizations_sub ] 30 | -------------------------------------------------------------------------------- /docker/php/php-fpm-fcgi.prod.ini: -------------------------------------------------------------------------------- 1 | expose_php = Off 2 | 3 | max_execution_time = 30 4 | max_input_time = 60 5 | memory_limit = 1536M 6 | error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT 7 | display_errors = Off 8 | display_startup_errors = Off 9 | 10 | post_max_size = 5M 11 | file_uploads = On 12 | upload_tmp_dir = /tmp 13 | upload_max_filesize = 5M 14 | max_file_uploads = 20 15 | 16 | date.timezone = UTC 17 | 18 | session.auto_start = Off 19 | session.name = PHPSESSID 20 | session.gc_probability = 1 21 | session.gc_divisor = 1000 22 | 23 | opcache.enable=1 24 | opcache.enable_cli=0 25 | opcache.memory_consumption=256 26 | opcache.interned_strings_buffer=32 27 | opcache.max_accelerated_files=16229 28 | opcache.validate_timestamps=0 29 | opcache.revalidate_path=1 30 | opcache.save_comments=1 31 | opcache.fast_shutdown=1 32 | opcache.max_wasted_percentage=5 33 | opcache.enable_file_override=1 34 | ;opcache.error_log=/var/log/php/opcache-error.log 35 | opcache.error_log=/proc/self/fd/2 36 | opcache.log_verbosity_level=2 37 | opcache.preload_user=www-data 38 | opcache.preload=/app/config/preload.php 39 | 40 | realpath_cache_size = 4096K 41 | realpath_cache_ttl = 600 42 | 43 | ;error_log = /var/log/php/error.log 44 | error_log = /proc/self/fd/2 45 | -------------------------------------------------------------------------------- /src/Application/MyOrganizations/Output/OrganizationsItemWithFleetOutput.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE TABLE organizations (id UUID NOT NULL, founder_id UUID NOT NULL, name VARCHAR(32) NOT NULL, sid VARCHAR(15) NOT NULL, logo_url VARCHAR(1023) DEFAULT NULL, updated_at TIMESTAMP(0) WITH TIME ZONE NOT NULL, version INT DEFAULT 1 NOT NULL, PRIMARY KEY(id))'); 13 | $this->addSql('CREATE UNIQUE INDEX UNIQ_427C1C7F57167AB4 ON organizations (sid)'); 14 | $this->addSql('CREATE INDEX founder_idx ON organizations (founder_id)'); 15 | $this->addSql('COMMENT ON COLUMN organizations.id IS \'(DC2Type:ulid)\''); 16 | $this->addSql('COMMENT ON COLUMN organizations.founder_id IS \'(DC2Type:ulid)\''); 17 | $this->addSql('COMMENT ON COLUMN organizations.updated_at IS \'(DC2Type:datetimetz_immutable)\''); 18 | } 19 | 20 | public function down(Schema $schema): void 21 | { 22 | $this->addSql('CREATE SCHEMA public'); 23 | $this->addSql('DROP TABLE organizations'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Application/MyFleet/UpdateShipService.php: -------------------------------------------------------------------------------- 1 | fleetRepository->getFleetByUser($userId); 29 | if ($fleet === null) { 30 | throw new NotFoundFleetByUserException($userId); 31 | } 32 | 33 | $fleet->updateShip($shipId, $name, $imageUrl, $quantity, $this->clock->now()); 34 | 35 | $this->fleetRepository->save($fleet); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Form/PatchNoteForm.php: -------------------------------------------------------------------------------- 1 | add('title', TextType::class, [ 19 | 'required' => true, 20 | ]) 21 | ->add('body', TextareaType::class, [ 22 | 'required' => true, 23 | ]) 24 | ->add('link', UrlType::class, [ 25 | 'required' => false, 26 | ]); 27 | } 28 | 29 | public function configureOptions(OptionsResolver $resolver): void 30 | { 31 | $resolver->setDefaults([ 32 | 'data_class' => PatchNote::class, 33 | 'allow_extra_fields' => true, 34 | 'csrf_protection' => true, 35 | ]); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /fixtures/monthly_cost_coverage.yaml: -------------------------------------------------------------------------------- 1 | App\Entity\MonthlyCostCoverage: 2 | default: 3 | __construct: 4 | - '<(new \App\Domain\MonthlyCostCoverageId(\Symfony\Component\Uid\Ulid::fromString("01F17Y8TPE5JBC6RNM3DVVMD11")))>' 5 | - '<(new \DateTimeImmutable("1970-01-01 00:00:00"))>' 6 | target: 10000 # $100.00 7 | postpone: true 8 | last_month: 9 | __construct: 10 | - '<(new \App\Domain\MonthlyCostCoverageId(\Symfony\Component\Uid\Ulid::fromString("01F17YAEF6VZ9J951XCQTZRW2K")))>' 11 | - '<(new \DateTimeImmutable("first day of last month"))>' 12 | target: 20000 13 | postpone: false 14 | this_month: 15 | __construct: 16 | - '<(new \App\Domain\MonthlyCostCoverageId(\Symfony\Component\Uid\Ulid::fromString("01F17YAH6WGESK19R9CPG7Z4D0")))>' 17 | - '<(new \DateTimeImmutable("first day of"))>' 18 | target: 15000 19 | postpone: true 20 | next_month: 21 | __construct: 22 | - '<(new \App\Domain\MonthlyCostCoverageId(\Symfony\Component\Uid\Ulid::fromString("01F17YAMEJEDDMCE4R2NHZ5A34")))>' 23 | - '<(new \DateTimeImmutable("first day of next month"))>' 24 | target: 20000 25 | postpone: false 26 | -------------------------------------------------------------------------------- /src/Application/MyOrganizations/UpdateOrganizationService.php: -------------------------------------------------------------------------------- 1 | organizationRepository->getOrganization($orgaId); 23 | if ($organization === null) { 24 | throw new NotFoundOrganizationException($orgaId); 25 | } 26 | if (!$organization->isFounder($founderId)) { 27 | throw new NotFounderOfOrganizationException($orgaId, $founderId); 28 | } 29 | 30 | $organization->update($name, $logoUrl, $this->clock->now()); 31 | 32 | $this->organizationRepository->save($organization); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Application/ShipTemplate/CreateTemplateService.php: -------------------------------------------------------------------------------- 1 | model, 26 | $input->pictureUrl, 27 | $input->chassis, 28 | $input->manufacturer, 29 | $input->size, 30 | $input->role, 31 | $input->cargoCapacity, 32 | $input->crew, 33 | $input->price, 34 | $this->clock->now(), 35 | ); 36 | 37 | $this->shipTemplateRepository->save($template); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docker/php/php-fpm-fcgi.dev.ini: -------------------------------------------------------------------------------- 1 | expose_php = Off 2 | 3 | max_execution_time = 30 4 | max_input_time = 60 5 | memory_limit = 1536M 6 | error_reporting = E_ALL 7 | display_errors = On 8 | display_startup_errors = On 9 | 10 | post_max_size = 5M 11 | file_uploads = On 12 | upload_tmp_dir = /tmp 13 | upload_max_filesize = 5M 14 | max_file_uploads = 20 15 | 16 | date.timezone = UTC 17 | 18 | session.auto_start = Off 19 | session.name = PHPSESSID 20 | session.gc_probability = 1 21 | session.gc_divisor = 1000 22 | 23 | opcache.enable=1 24 | opcache.enable_cli=0 25 | opcache.memory_consumption=256 26 | opcache.interned_strings_buffer=32 27 | opcache.max_accelerated_files=16229 28 | opcache.validate_timestamps=1 29 | opcache.revalidate_freq=0 30 | opcache.revalidate_path=1 31 | opcache.save_comments=1 32 | opcache.fast_shutdown=1 33 | opcache.max_wasted_percentage=5 34 | opcache.enable_file_override=1 35 | ;opcache.error_log=/var/log/php/opcache-error.log 36 | opcache.error_log=/proc/self/fd/2 37 | opcache.log_verbosity_level=2 38 | ;opcache.preload_user=www-data 39 | ;opcache.preload=/app/var/cache/dev/App_KernelDevDebugContainer.preload.php 40 | 41 | realpath_cache_size = 4096K 42 | realpath_cache_ttl = 600 43 | 44 | ;error_log = /var/log/php/error.log 45 | error_log = /proc/self/fd/2 46 | -------------------------------------------------------------------------------- /src/Entity/Manufacturer.php: -------------------------------------------------------------------------------- 1 | name = $name; 33 | $this->code = $code; 34 | if ($this->code !== null) { 35 | $this->code = u($this->code)->upper(); 36 | } 37 | } 38 | 39 | public function getName(): ?string 40 | { 41 | return $this->name; 42 | } 43 | 44 | public function getCode(): ?string 45 | { 46 | return $this->code; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Application/Support/GiveFeedbackService.php: -------------------------------------------------------------------------------- 1 | userRepository->getById($userId); 23 | if ($user === null) { 24 | throw new NotFoundUserException($userId); 25 | } 26 | 27 | $this->notifier->send(new FeedbackNotification( 28 | $description, 29 | $user->getNickname(), 30 | $user->getCoins(), 31 | $profile->getEmail(), 32 | $profile->getDiscordId(), 33 | $user->getCreatedAt(), 34 | $email, 35 | $discordId, 36 | )); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Service/Funding/VerifyWebhookSignatureFactory.php: -------------------------------------------------------------------------------- 1 | webhookId = $webhookId; 15 | } 16 | 17 | public function createVerifyWebhookSignature(Request $request): VerifyWebhookSignature 18 | { 19 | $signatureVerification = new VerifyWebhookSignature(); 20 | $signatureVerification->setAuthAlgo($request->headers->get('paypal-auth-algo')); 21 | $signatureVerification->setTransmissionId($request->headers->get('paypal-transmission-id')); 22 | $signatureVerification->setCertUrl($request->headers->get('paypal-cert-url')); 23 | $signatureVerification->setWebhookId($this->webhookId); 24 | $signatureVerification->setTransmissionSig($request->headers->get('paypal-transmission-sig')); 25 | $signatureVerification->setTransmissionTime($request->headers->get('paypal-transmission-time')); 26 | $signatureVerification->setRequestBody($request->getContent()); 27 | 28 | return $signatureVerification; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Infrastructure/Service/InMemoryEntityIdGenerator.php: -------------------------------------------------------------------------------- 1 | uid = '00000000-0000-0000-0000-000000000001'; 17 | $this->callback = static function (string $oldUid): string { 18 | $n = hexdec(substr($oldUid, -12)); 19 | 20 | return substr($oldUid, 0, -12).sprintf('%012s', dechex($n + 1)); 21 | }; 22 | } 23 | 24 | public function setUid(string $uid): void 25 | { 26 | Assert::uuid($uid); 27 | $this->uid = $uid; 28 | } 29 | 30 | public function setNextUidGenerator(callable $callback): void 31 | { 32 | $this->callback = $callback; 33 | } 34 | 35 | public function generateEntityId(string $class): EntityId 36 | { 37 | Assert::isAOf($class, EntityId::class); 38 | 39 | $id = $class::fromString($this->uid); 40 | 41 | $this->uid = ($this->callback)($this->uid); 42 | 43 | return $id; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Application/MyOrganizations/DisbandOrganizationService.php: -------------------------------------------------------------------------------- 1 | organizationRepository->getOrganization($orgaId); 23 | if ($organization === null) { 24 | throw new NotFoundOrganizationException($orgaId); 25 | } 26 | if (!$organization->isFounder($founderId)) { 27 | throw new NotFounderOfOrganizationException($orgaId, $founderId); 28 | } 29 | 30 | $this->organizationRepository->deleteAll([$orgaId]); 31 | $this->organizationFleetRepository->deleteAll([$orgaId]); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Infrastructure/Validator/UniqueOrganizationSidValidator.php: -------------------------------------------------------------------------------- 1 | organizationRepository->getOrganizationBySid($value); 27 | if ($orga === null) { 28 | return; 29 | } 30 | if ($constraint->excludeOrgaId !== null && $orga->getId()->equals($constraint->excludeOrgaId)) { 31 | // ignore if it's the excluded orgaId 32 | return; 33 | } 34 | $this->context->buildViolation($constraint->message)->addViolation(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Application/MyOrganizations/MyOrganizationsService.php: -------------------------------------------------------------------------------- 1 | organizationRepository->getOrganizationsByMember($memberId); 21 | 22 | return new MyOrganizationsOutput( 23 | array_map(static function (Organization $organization) use ($memberId): MyOrganizationsItemOutput { 24 | return new MyOrganizationsItemOutput( 25 | $organization->getId(), 26 | $organization->getName(), 27 | $organization->getSid(), 28 | $organization->getLogoUrl(), 29 | $organization->hasJoined($memberId), 30 | ); 31 | }, $organizations), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Entity/Crew.php: -------------------------------------------------------------------------------- 1 | min = $min !== null ? (string) $min : null; 35 | $this->max = $max !== null ? (string) $max : null; 36 | } 37 | 38 | public function getMin(): ?int 39 | { 40 | return $this->min !== null ? (int) $this->min : null; 41 | } 42 | 43 | public function getMax(): ?int 44 | { 45 | return $this->max !== null ? (int) $this->max : null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /migrations/Version20210522155300.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE TABLE organization_fleet_member_versions (member_id UUID NOT NULL, organization_fleet_id UUID NOT NULL, version INT DEFAULT 1 NOT NULL, PRIMARY KEY(member_id, organization_fleet_id))'); 13 | $this->addSql('CREATE INDEX IDX_BC393CAA41EDF5B ON organization_fleet_member_versions (organization_fleet_id)'); 14 | $this->addSql('COMMENT ON COLUMN organization_fleet_member_versions.member_id IS \'(DC2Type:ulid)\''); 15 | $this->addSql('COMMENT ON COLUMN organization_fleet_member_versions.organization_fleet_id IS \'(DC2Type:ulid)\''); 16 | $this->addSql('ALTER TABLE organization_fleet_member_versions ADD CONSTRAINT FK_BC393CAA41EDF5B FOREIGN KEY (organization_fleet_id) REFERENCES organization_fleets (orga_id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); 17 | } 18 | 19 | public function down(Schema $schema): void 20 | { 21 | $this->addSql('CREATE SCHEMA public'); 22 | $this->addSql('DROP TABLE organization_fleet_member_versions'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Infrastructure/Controller/Home/NumbersController.php: -------------------------------------------------------------------------------- 1 | numbersService->handle(); 32 | 33 | $json = $this->serializer->serialize($output, 'json'); 34 | 35 | return (new JsonResponse($json, 200, [], true))->setSharedMaxAge(300); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /config/packages/jwt_auth.yaml: -------------------------------------------------------------------------------- 1 | jwt_auth: 2 | # The domain of your registered Auth0 tenant. 3 | domain: "%env(AUTH0_DOMAIN)%" 4 | # The client ID string of your registered Auth0 application. 5 | client_id: "%env(AUTH0_CLIENT_ID)%" 6 | # The audience/identifier string of your registered Auth0 API. 7 | audience: "%env(AUTH0_API_AUDIENCE)%" 8 | 9 | # Defaults to RS256. Supported options are RS256 or HS256. 10 | algorithm: "RS256" 11 | # If you're using HS256, you need to provide the client secret for your registered Auth0 application. 12 | # client_secret: "%env(AUTH0_CLIENT_SECRET)%" 13 | 14 | cache: "cache.app" 15 | 16 | # Token validations to run during JWT decoding: 17 | validations: 18 | # Validate AUD claim against a value, such as an API identifier. Set to false to skip. Defaults to jwt_auth.audience. 19 | aud: "%env(AUTH0_API_AUDIENCE)%" 20 | # Validate the AZP claim against a value, such as a client ID. Set to false to skip. Defaults to false. 21 | azp: "%env(AUTH0_CLIENT_ID)%" 22 | # Maximum age (in seconds) since the auth_time of the token. Set to false to skip. Defaults to false. 23 | max_age: 3600 24 | # Clock tolerance (in seconds) for token expiration checks. Requires an integer value. Defaults to 60 seconds. 25 | leeway: 60 26 | -------------------------------------------------------------------------------- /src/Application/MyFleet/MyFleetService.php: -------------------------------------------------------------------------------- 1 | fleetRepository->getFleetByUser($userId); 22 | if ($fleet === null) { 23 | throw new NotFoundFleetByUserException($userId); 24 | } 25 | 26 | $shipOutputItems = []; 27 | foreach ($fleet->getShips() as $ship) { 28 | $shipOutputItems[] = new MyFleetShipOutput($ship->getId(), $ship->getModel(), $ship->getImageUrl(), $ship->getQuantity(), $ship->getTemplateId()); 29 | } 30 | 31 | return new MyFleetOutput( 32 | ships: new MyFleetShipsCollectionOutput(items: $shipOutputItems, count: count($shipOutputItems)), 33 | updatedAt: $fleet->getUpdatedAt(), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Application/Repository/OrganizationRepositoryInterface.php: -------------------------------------------------------------------------------- 1 | import('../config/{packages}/*.{yaml,php}'); 17 | $container->import('../config/{packages}/'.$this->environment.'/*.{yaml,php}'); 18 | 19 | $container->import('../config/services.yaml'); 20 | $container->import('../config/{services}_'.$this->environment.'.yaml'); 21 | } 22 | 23 | protected function configureRoutes(RoutingConfigurator $routes): void 24 | { 25 | $routes->import('../config/{routes}/'.$this->environment.'/*.yaml'); 26 | $routes->import('../config/{routes}/*.yaml'); 27 | } 28 | 29 | public function getBuildDir(): string 30 | { 31 | if (isset($_SERVER['APP_BUILD_DIR'])) { 32 | return $_SERVER['APP_BUILD_DIR'].'/'.$this->environment; 33 | } 34 | 35 | return $this->getProjectDir().'/var/build/'.$this->environment; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | tests/Unit 20 | 21 | 22 | tests/Acceptance 23 | 24 | 25 | tests/Integration 26 | 27 | 28 | tests/End2End 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/Infrastructure/Controller/MyFleet/ClearMyFleetController.php: -------------------------------------------------------------------------------- 1 | security->isGranted('ROLE_USER')) { 30 | throw new AccessDeniedException(); 31 | } 32 | 33 | /** @var User $user */ 34 | $user = $this->security->getUser(); 35 | 36 | $this->clearMyFleetService->handle($user->getId()); 37 | 38 | return new Response(null, 204); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Application/ShipTemplate/Output/ListTemplatesItemOutput.php: -------------------------------------------------------------------------------- 1 | templates[(string) $templateId] ?? null; 18 | } 19 | 20 | public function save(ShipTemplate $template): void 21 | { 22 | $this->templates[(string) $template->getId()] = $template; 23 | } 24 | 25 | /** 26 | * {@inheritDoc} 27 | */ 28 | public function getTemplatesOfAuthor(TemplateAuthorId $authorId): array 29 | { 30 | $templates = array_values(array_filter($this->templates, static function (ShipTemplate $template) use ($authorId): bool { 31 | return $template->getAuthorId()->equals($authorId); 32 | })); 33 | usort($templates, static function (ShipTemplate $template1, ShipTemplate $template2): int { 34 | return $template1->getModel() <=> $template2->getModel(); 35 | }); 36 | 37 | return $templates; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Infrastructure/Validator/CountOrganizationsLessThanValidator.php: -------------------------------------------------------------------------------- 1 | security->getUser(); 31 | Assert::notNull($user); 32 | 33 | $orgas = $this->organizationRepository->getOrganizationsOfFounder(MemberId::fromString((string) $user->getId())); 34 | if (count($orgas) >= $constraint->max) { 35 | $this->context->buildViolation($constraint->message)->addViolation(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Entity/Membership.php: -------------------------------------------------------------------------------- 1 | memberId = $memberId->getId(); 36 | $this->organization = $organization; 37 | $this->joined = $joined; 38 | } 39 | 40 | public function getMemberId(): MemberId 41 | { 42 | return new MemberId($this->memberId); 43 | } 44 | 45 | public function hasJoined(): bool 46 | { 47 | return $this->joined; 48 | } 49 | 50 | public function accept(): void 51 | { 52 | $this->joined = true; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/End2End/Controller/PatchNote/HasNewPatchNoteControllerTest.php: -------------------------------------------------------------------------------- 1 | executeStatement(<< 'application/json', 24 | 'HTTP_AUTHORIZATION' => 'Bearer '.static::generateToken('Ioni'), 25 | ]); 26 | 27 | static::assertSame(200, static::$client->getResponse()->getStatusCode()); 28 | $json = static::json(); 29 | static::assertSame([ 30 | 'hasNewPatchNote' => true, 31 | ], $json); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Application/MyFleet/ImportFleetService.php: -------------------------------------------------------------------------------- 1 | fleetRepository->getFleetByUser($userId); 28 | if ($fleet === null) { 29 | $fleet = new Fleet($userId, $this->clock->now()); 30 | } 31 | 32 | $fleetShipImports = []; 33 | foreach ($importFleetShips as $importFleetShip) { 34 | $fleetShipImports[] = new FleetShipImport($importFleetShip->model); 35 | } 36 | $fleet->importShips($fleetShipImports, $onlyMissing, $this->clock->now(), $this->entityIdGenerator); 37 | 38 | $this->fleetRepository->save($fleet); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Infrastructure/Controller/Profile/Input/ChangeHandleInput.php: -------------------------------------------------------------------------------- 1 | handle = $data['handle'] ?? null; 31 | if ($this->handle !== null) { 32 | $this->handle = u($this->handle)->trim()->lower(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Serializer/EntityIdNormalizer.php: -------------------------------------------------------------------------------- 1 | symfony/framework-bundle ### 2 | APP_ENV=dev 3 | APP_SECRET=4b75b543292357158424363c5f2e8c6d 4 | ###< symfony/framework-bundle ### 5 | 6 | ###> sentry/sentry-symfony ### 7 | SENTRY_DSN= 8 | ###< sentry/sentry-symfony ### 9 | 10 | ###> symfony/mailer ### 11 | MAILER_DSN=smtp://smtp:25 12 | ###< symfony/mailer ### 13 | 14 | FUNDING_ORDER_CAPTURE_ADDRESSES=fleet-manager@protonmail.com 15 | MONOLOG_MAILER_TO_0=fleet-manager@protonmail.com 16 | 17 | ###> doctrine/doctrine-bundle ### 18 | DATABASE_URL="postgresql://postgres:root@postgres:5432/fleet_manager?serverVersion=13&charset=utf8" 19 | ###< doctrine/doctrine-bundle ### 20 | 21 | REDIS_DSN=redis://root@redis:6379 22 | 23 | PAYPAL_CHECKOUT_CLIENT_ID= 24 | PAYPAL_CHECKOUT_CLIENT_SECRET= 25 | PAYPAL_CHECKOUT_WEBHOOK_ID= 26 | PAYPAL_CHECKOUT_MODE=sandbox 27 | 28 | ###> symfony/messenger ### 29 | # MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages 30 | MESSENGER_TRANSPORT_FAILED_DSN=doctrine://default?queue_name=failed 31 | MESSENGER_TRANSPORT_ORGANIZATIONS_SUB_DSN=doctrine://default?queue_name=organizations_events 32 | ###< symfony/messenger ### 33 | 34 | DEFAULT_URI=http://localhost:8000 35 | 36 | # Auth0 JWT 37 | AUTH0_DOMAIN= 38 | AUTH0_CLIENT_ID= 39 | AUTH0_API_AUDIENCE= 40 | 41 | AUTH0_CLIENT_ID_API= 42 | AUTH0_CLIENT_SECRET_API= 43 | 44 | ###> symfony/discord-notifier ### 45 | DISCORD_DSN=discord://TOKEN@default?webhook_id=ID 46 | ###< symfony/discord-notifier ### 47 | -------------------------------------------------------------------------------- /migrations/Version20210506210323.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE TABLE ship_templates (id UUID NOT NULL, author_id UUID NOT NULL, model VARCHAR(60) NOT NULL, image_url VARCHAR(1023) DEFAULT NULL, updated_at TIMESTAMP(0) WITH TIME ZONE NOT NULL, version INT DEFAULT 1 NOT NULL, chassis_name VARCHAR(60) NOT NULL, manufacturer_name VARCHAR(50) DEFAULT NULL, manufacturer_code VARCHAR(5) DEFAULT NULL, ship_size_size VARCHAR(10) DEFAULT NULL, ship_role_role VARCHAR(30) DEFAULT NULL, cargo_capacity_capacity INT NOT NULL, crew_min NUMERIC(10, 0) DEFAULT NULL, crew_max NUMERIC(10, 0) DEFAULT NULL, price_pledge NUMERIC(12, 2) DEFAULT NULL, price_ingame NUMERIC(12, 2) DEFAULT NULL, PRIMARY KEY(id))'); 13 | $this->addSql('COMMENT ON COLUMN ship_templates.id IS \'(DC2Type:ulid)\''); 14 | $this->addSql('COMMENT ON COLUMN ship_templates.author_id IS \'(DC2Type:ulid)\''); 15 | $this->addSql('COMMENT ON COLUMN ship_templates.updated_at IS \'(DC2Type:datetimetz_immutable)\''); 16 | } 17 | 18 | public function down(Schema $schema): void 19 | { 20 | $this->addSql('CREATE SCHEMA public'); 21 | $this->addSql('DROP TABLE ship_templates'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Poubelle/BackOffice/PatchNote/PatchNoteCreateControllerTest.php: -------------------------------------------------------------------------------- 1 | client->request('GET', '/bo/patch-note/create'); 17 | 18 | static::assertSame(403, $this->client->getResponse()->getStatusCode()); 19 | } 20 | 21 | /** 22 | * @group functional 23 | * @group patch_note 24 | * @group bo 25 | */ 26 | public function test_not_admin(): void 27 | { 28 | $this->client->request('GET', '/bo/patch-note/create', [], [], [ 29 | 'HTTP_AUTHORIZATION' => 'Bearer '.static::generateToken('Gardien1'), 30 | ]); 31 | 32 | static::assertSame(403, $this->client->getResponse()->getStatusCode()); 33 | } 34 | 35 | /** 36 | * @group functional 37 | * @group patch_note 38 | * @group bo 39 | */ 40 | public function test_admin(): void 41 | { 42 | $this->client->request('GET', '/bo/patch-note/create', [], [], [ 43 | 'HTTP_AUTHORIZATION' => 'Bearer '.static::generateToken('Ioni'), 44 | ]); 45 | 46 | static::assertSame(200, $this->client->getResponse()->getStatusCode()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], 6 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], 7 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], 8 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], 9 | Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], 10 | Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true, 'test_acceptance' => true], 11 | Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['dev' => true], 12 | Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true], 13 | Hautelook\AliceBundle\HautelookAliceBundle::class => ['dev' => true], 14 | Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], 15 | Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], 16 | Sentry\SentryBundle\SentryBundle::class => ['prod' => true, 'beta' => true], 17 | Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], 18 | Http\HttplugBundle\HttplugBundle::class => ['all' => true], 19 | Auth0\JWTAuthBundle\JWTAuthBundle::class => ['all' => true], 20 | Nelmio\ApiDocBundle\NelmioApiDocBundle::class => ['dev' => true], 21 | ]; 22 | -------------------------------------------------------------------------------- /src/Infrastructure/Controller/ShipTemplate/Input/CreateTemplateManufacturerInput.php: -------------------------------------------------------------------------------- 1 | name = u($data['name'])->trim(); 29 | } 30 | if (isset($data['code'])) { 31 | $this->code = u($data['code'])->trim()->upper(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Application/MyOrganizations/UnjoinOrganizationService.php: -------------------------------------------------------------------------------- 1 | organizationRepository->getOrganization($orgaId); 24 | if ($organization === null) { 25 | throw new NotFoundOrganizationException($orgaId); 26 | } 27 | 28 | if (!$organization->isMemberOf($memberId)) { 29 | throw new NotMemberOfOrganizationException($orgaId, $memberId); 30 | } 31 | 32 | if ($organization->hasJoined($memberId)) { 33 | throw new FullyJoinedMemberOfOrganizationException($orgaId, $memberId); 34 | } 35 | 36 | $organization->unjoinMember($memberId, $this->clock->now()); 37 | 38 | $this->organizationRepository->save($organization); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /templates/back_office/funding/monthly_cost_coverage_create.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'back_office_layout.html.twig' %} 2 | 3 | {% block body %} 4 |
5 |

Monthly Cost Coverage Create

6 | 7 |

Back to the list

8 | 9 |
10 | {% if form.month is defined %} 11 | 12 | {{ form_errors((form.month)) }} 13 |
14 |
15 | {{ form_widget(form.month.year) }} 16 | {{ form_widget(form.month.month) }} 17 |
18 |
19 | {% endif %} 20 | {{ form_row(form.target) }} 21 | {% if form.postpone is defined %} 22 | {{ form_row(form.postpone) }} 23 | {% endif %} 24 | 25 | 26 | {% if form._token is defined %} 27 | {{ form_widget(form._token) }} 28 | {% endif %} 29 |
30 |
31 | {% endblock %} 32 | 33 | {% block javascripts %} 34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(['--env', '-e'], null, true)) { 24 | putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); 25 | } 26 | 27 | if ($input->hasParameterOption('--no-debug', true)) { 28 | putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); 29 | } 30 | 31 | (new Dotenv())->bootEnv(dirname(__DIR__).'/.env'); 32 | 33 | if ($_SERVER['APP_DEBUG']) { 34 | umask(0000); 35 | 36 | if (class_exists(Debug::class)) { 37 | Debug::enable(); 38 | } 39 | } 40 | 41 | $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); 42 | $application = new Application($kernel); 43 | $application->run($input); 44 | -------------------------------------------------------------------------------- /src/Application/MyFleet/UpdateShipFromTemplateService.php: -------------------------------------------------------------------------------- 1 | fleetRepository->getFleetByUser($userId); 26 | if ($fleet === null) { 27 | $fleet = new Fleet($userId, $this->clock->now()); 28 | } 29 | 30 | $template = $this->listTemplatesProvider->getShipTemplateOfUser($templateId, $userId); 31 | if ($template === null) { 32 | throw new NotFoundShipTemplateByUserException($userId, $templateId); 33 | } 34 | 35 | $fleet->updateShipFromTemplate($shipId, $template, $quantity ?? 1, $this->clock->now()); 36 | 37 | $this->fleetRepository->save($fleet); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Application/MyOrganizations/OrganizationsService.php: -------------------------------------------------------------------------------- 1 | organizationRepository->getOrganizations($itemsPerPage, $sinceOrgaId, $searchQuery); 21 | 22 | return new OrganizationsCollectionOutput( 23 | array_map(static function (Organization $organization): OrganizationsItemOutput { 24 | return new OrganizationsItemOutput( 25 | $organization->getId(), 26 | $organization->getName(), 27 | $organization->getSid(), 28 | $organization->getLogoUrl(), 29 | ); 30 | }, $organizations), 31 | count($organizations) === $itemsPerPage ? $baseUrl.'?sinceId='.end($organizations)->getId() : null, 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Infrastructure/Controller/Profile/DeleteAccountController.php: -------------------------------------------------------------------------------- 1 | security->isGranted('ROLE_USER')) { 32 | throw new AccessDeniedException(); 33 | } 34 | 35 | /** @var User $user */ 36 | $user = $this->security->getUser(); 37 | 38 | $this->deleteAccount->handle($user->getId()); 39 | 40 | return new Response(null, 204); 41 | } 42 | } 43 | --------------------------------------------------------------------------------