From cb8ac3a08e1991cb604c7ecde32260acf433acf0 Mon Sep 17 00:00:00 2001 From: Flo Date: Sun, 4 Aug 2024 14:23:33 +0000 Subject: [PATCH] first commit --- .env.example | 13 + .gitattributes | 7 + .github/workflows/create branch on issue.yml | 40 +++ .github/workflows/php.yml | 39 +++ .gitignore | 15 + .htaccess | 3 + COPYRIGHT.md | 1 + LICENSE.md | 26 ++ README.md | 0 bin/clear-config-cache.php | 39 +++ bin/console.php | 30 ++ bin/createApi.php | 312 ++++++++++++++++++ bin/createPipeline.php | 194 +++++++++++ bin/doctrine-migrations-log.php | 91 +++++ bin/doctrine-migrations.php | 91 +++++ bin/script/build | 3 + bin/script/down | 3 + bin/script/exec | 23 ++ bin/script/init | 32 ++ bin/script/stop | 3 + bin/script/up | 3 + composer.development.json | 130 ++++++++ composer.json | 72 ++++ config/autoload/.gitignore | 2 + config/autoload/api.global.php | 29 ++ config/autoload/authorization.global.php | 22 ++ config/autoload/defines.php | 6 + config/autoload/dependencies.global.php | 26 ++ config/autoload/doctrine.global.php | 14 + config/autoload/logger.global.php | 14 + config/autoload/mezzio.global.php | 24 ++ config/config.php | 97 ++++++ config/container.php | 21 ++ config/development.config.php | 31 ++ config/pipeline.php | 94 ++++++ config/routes.php | 52 +++ data/migrations/log/Version20230922150649.php | 40 +++ .../myTube/Version20230922085011.php | 32 ++ .../myTube/Version20230922092351.php | 35 ++ .../myTube/Version20230922092754.php | 36 ++ .../myTube/Version20230922101354.php | 41 +++ .../myTube/Version20230922101355.php | 38 +++ .../myTube/Version20230924113403.php | 37 +++ .../myTube/Version20240214194156.php | 38 +++ .../myTube/Version20240223130626.php | 37 +++ .../myTube/Version20240223130835.php | 38 +++ .../myTube/Version20240223142111.php | 35 ++ .../myTube/Version20240229205003.php | 37 +++ .../myTube/Version20240323095155.php | 35 ++ docker/docker-compose-mac.yml | 51 +++ docker/docker-compose.yml | 51 +++ docker/mysql/dockerfile | 7 + docker/mysql/scripts/initdb.sql | 4 + docker/nginx/config/nginx.conf | 17 + docker/nginx/config/upstream.conf | 3 + docker/nginx/dockerfile | 5 + docker/php/config/uploads.ini | 6 + docker/php/dockerfile | 18 + phpunit.xml.dist | 26 ++ public/.htaccess | 19 ++ public/index.php | 48 +++ src/ApiDomain/Console/config/console.php | 15 + .../Console/config/service_manager.php | 16 + .../src/Command/AnalyzeVideoTitlesCommand.php | 64 ++++ .../src/Command/InitializeDataCommand.php | 80 +++++ .../Console/src/Command/RbacUpdateCommand.php | 98 ++++++ .../src/Command/ReadUntaggedVideosCommand.php | 64 ++++ src/ApiDomain/Console/src/ConfigProvider.php | 16 + .../External/Authentication/config/routes.php | 43 +++ .../Authentication/config/service_manager.php | 17 + .../Authentication/src/ConfigProvider.php | 16 + .../Handler/ConfirmRegistrationHandler.php | 42 +++ .../src/Handler/LoginUserHandler.php | 40 +++ .../src/Handler/LogoutUserHandler.php | 35 ++ .../src/Handler/RegisterUserHandler.php | 43 +++ .../External/Health/config/routes.php | 16 + .../Health/config/service_manager.php | 12 + .../External/Health/src/ConfigProvider.php | 16 + .../Health/src/Handler/HealthHandler.php | 23 ++ src/ApiDomain/External/Tag/config/routes.php | 41 +++ .../External/Tag/config/service_manager.php | 27 ++ .../External/Tag/src/ConfigProvider.php | 16 + .../Tag/src/Handler/AddAliasHandler.php | 44 +++ .../Tag/src/Handler/CreateHandler.php | 40 +++ .../Tag/src/Handler/ReadDetailsHandler.php | 41 +++ .../Tag/src/Handler/ReadVideoListHandler.php | 40 +++ .../AddAliasResponseFormatter.php | 25 ++ .../CreateResponseFormatter.php | 19 ++ .../ReadDetailsResponseFormatter.php | 32 ++ .../ReadVideoListResponseFormatter.php | 45 +++ .../External/TagList/config/routes.php | 16 + .../TagList/config/service_manager.php | 17 + .../External/TagList/src/ConfigProvider.php | 16 + .../TagList/src/Handler/ReadListHandler.php | 36 ++ .../ReadListResponseFormatter.php | 31 ++ src/ApiDomain/External/User/config/routes.php | 48 +++ .../External/User/config/service_manager.php | 21 ++ .../External/User/src/ConfigProvider.php | 16 + .../User/src/Formatter/UserFormatter.php | 27 ++ .../src/Handler/ChangePasswordHandler.php | 40 +++ .../src/Handler/ChangeUsernameHandler.php | 40 +++ .../User/src/Handler/CreateUserHandler.php | 42 +++ .../User/src/Handler/UserStateHandler.php | 34 ++ .../External/Video/config/routes.php | 68 ++++ .../External/Video/config/service_manager.php | 31 ++ .../External/Video/src/ConfigProvider.php | 16 + .../Video/src/Handler/DeleteHandler.php | 36 ++ .../Video/src/Handler/ReadDetailsHandler.php | 41 +++ .../Video/src/Handler/SetThumbnailHandler.php | 38 +++ .../Video/src/Handler/StreamHandler.php | 47 +++ .../Video/src/Handler/ThumbnailHandler.php | 47 +++ .../src/Handler/UpdateDetailsHandler.php | 38 +++ .../Video/src/Handler/UploadHandler.php | 41 +++ .../DeleteResponseFormatter.php | 15 + .../ReadDetailsResponseFormatter.php | 33 ++ .../SetThumbnailResponseFormatter.php | 15 + .../ThumbnailResponseFormatter.php | 15 + .../UpdateDetailsResponseFormatter.php | 15 + .../External/VideoList/config/routes.php | 23 ++ .../VideoList/config/service_manager.php | 17 + .../External/VideoList/src/ConfigProvider.php | 16 + .../VideoList/src/Handler/ReadListHandler.php | 39 +++ .../VideoList/src/Handler/UploadHandler.php | 41 +++ .../ReadListResponseFormatter.php | 45 +++ .../UploadResponseFormatter.php | 46 +++ src/DataDomain/Business/config/doctrine.php | 55 +++ .../Business/config/service_manager.php | 21 ++ .../Business/src/ConfigProvider.php | 16 + .../Business/src/Entity/Permission.php | 75 +++++ .../Business/src/Entity/Registration.php | 66 ++++ src/DataDomain/Business/src/Entity/Role.php | 79 +++++ src/DataDomain/Business/src/Entity/Tag.php | 119 +++++++ .../Business/src/Entity/TagAlias.php | 90 +++++ src/DataDomain/Business/src/Entity/User.php | 148 +++++++++ .../Business/src/Entity/UserSession.php | 105 ++++++ src/DataDomain/Business/src/Entity/Video.php | 130 ++++++++ .../Business/src/Entity/VideoEvent.php | 84 +++++ .../Factory/MyTubeEntityManagerFactory.php | 19 ++ .../src/Manager/MyTubeEntityManager.php | 11 + .../src/Repository/PermissionRepository.php | 10 + .../src/Repository/ProductRepository.php | 10 + .../src/Repository/RegistrationRepository.php | 24 ++ .../src/Repository/RoleRepository.php | 10 + .../src/Repository/TagAliasRepository.php | 8 + .../Business/src/Repository/TagRepository.php | 24 ++ .../src/Repository/UserRepository.php | 26 ++ .../src/Repository/UserSessionRepository.php | 22 ++ .../src/Repository/VideoEventRepository.php | 8 + .../src/Repository/VideoRepository.php | 58 ++++ src/DataDomain/Log/config/doctrine.php | 55 +++ src/DataDomain/Log/config/service_manager.php | 16 + src/DataDomain/Log/src/ConfigProvider.php | 16 + src/DataDomain/Log/src/Entity/Log.php | 129 ++++++++ .../src/Factory/LogEntityManagerFactory.php | 21 ++ .../Log/src/Manager/LogEntityManager.php | 11 + .../Log/src/Repository/LogRepository.php | 8 + .../Registration/config/service_manager.php | 53 +++ .../src/Builder/RegistrationBuilder.php | 19 ++ .../Registration/src/ConfigProvider.php | 15 + .../RegistrationNotFoundByIdException.php | 25 ++ ...onWithIdentifierAlreadyExistsException.php | 25 ++ .../ConfirmRegistrationCommand.php | 28 ++ .../ConfirmRegistrationCommandBuilder.php | 21 ++ .../ConfirmRegistrationCommandHandler.php | 23 ++ .../RegisterUser/RegisterUserCommand.php | 25 ++ .../RegisterUserCommandBuilder.php | 19 ++ .../RegisterUserCommandHandler.php | 23 ++ .../ConfirmRegistrationPayload.php | 46 +++ .../ConfirmRegistrationPipeline.php | 26 ++ .../Step/CreateUserStep.php | 60 ++++ .../Step/LoadRegistrationStep.php | 47 +++ .../Step/SaveRegistrationAndUserStep.php | 32 ++ .../RegisterUser/RegisterUserPayload.php | 33 ++ .../RegisterUser/RegisterUserPipeline.php | 29 ++ .../Step/BuildRegistrationStep.php | 34 ++ .../RegisterUser/Step/CheckIdentifierStep.php | 41 +++ .../Step/SaveRegistrationStep.php | 30 ++ .../RegisterUser/Step/SendMailStep.php | 49 +++ ...trationWithIdentifierAlreadyExistsRule.php | 29 ++ .../RoleNotFoundByIdentifierException.php | 24 ++ .../Tag/config/service_manager.php | 44 +++ .../Tag/src/Builder/TagAliasBuilder.php | 21 ++ .../Tag/src/Builder/TagBuilder.php | 18 + src/HandlingDomain/Tag/src/ConfigProvider.php | 15 + .../TagAliasAlreadyExistsException.php | 25 ++ .../Exception/TagAlreadyExistsException.php | 25 ++ .../Exception/TagNotFoundByIdException.php | 25 ++ .../Command/AddAlias/AddAliasCommand.php | 26 ++ .../AddAlias/AddAliasCommandBuilder.php | 20 ++ .../AddAlias/AddAliasCommandHandler.php | 63 ++++ .../AddAlias/AddAliasCommandResult.php | 20 ++ .../Handler/Command/Create/CreateCommand.php | 18 + .../Command/Create/CreateCommandBuilder.php | 16 + .../Command/Create/CreateCommandHandler.php | 39 +++ .../Command/Create/CreateCommandResult.php | 20 ++ .../Query/ReadDetails/ReadDetailsQuery.php | 20 ++ .../ReadDetails/ReadDetailsQueryBuilder.php | 18 + .../ReadDetails/ReadDetailsQueryHandler.php | 39 +++ .../ReadDetails/ReadDetailsQueryResult.php | 20 ++ .../ReadVideoList/ReadVideoListQuery.php | 38 +++ .../ReadVideoListQueryBuilder.php | 24 ++ .../ReadVideoListQueryHandler.php | 52 +++ .../ReadVideoListQueryResult.php | 20 ++ .../Tag/src/Rule/TagAliasExistsRule.php | 27 ++ .../Tag/src/Rule/TagExistsRule.php | 28 ++ .../TagList/config/service_manager.php | 17 + .../TagList/src/ConfigProvider.php | 15 + .../Handler/Query/ReadList/ReadListQuery.php | 18 + .../Query/ReadList/ReadListQueryBuilder.php | 16 + .../Query/ReadList/ReadListQueryHandler.php | 31 ++ .../Query/ReadList/ReadListQueryResult.php | 18 + .../User/config/service_manager.php | 39 +++ .../User/src/Builder/UserBuilder.php | 53 +++ .../User/src/ConfigProvider.php | 15 + .../UserNotFoundByIdentifierException.php | 26 ++ .../UserPasswordMismatchException.php | 21 ++ .../UserWithIdentifierAlreadyExists.php | 24 ++ .../Exception/UserWrongPasswordException.php | 21 ++ .../ChangePassword/ChangePasswordCommand.php | 27 ++ .../ChangePasswordCommandBuilder.php | 21 ++ .../ChangePasswordCommandHandler.php | 39 +++ .../ChangeUsername/ChangeUsernameCommand.php | 28 ++ .../ChangeUsernameCommandBuilder.php | 21 ++ .../ChangeUsernameCommandHandler.php | 30 ++ .../Command/CreateUser/CreateUserCommand.php | 25 ++ .../CreateUser/CreateUserCommandBuilder.php | 19 ++ .../CreateUser/CreateUserCommandHandler.php | 39 +++ .../User/src/Rule/UserPasswordMatchRule.php | 15 + .../UserWithIdentifierAlreadyExistsRule.php | 29 ++ .../UserSession/config/service_manager.php | 31 ++ .../src/Builder/UserSessionBuilder.php | 17 + .../UserSession/src/ConfigProvider.php | 15 + .../Command/LoginUser/LoginUserCommand.php | 29 ++ .../LoginUser/LoginUserCommandBuilder.php | 23 ++ .../LoginUser/LoginUserCommandHandler.php | 80 +++++ .../Command/LogoutUser/LogoutUserCommand.php | 19 ++ .../LogoutUser/LogoutUserCommandBuilder.php | 19 ++ .../LogoutUser/LogoutUserCommandHandler.php | 37 +++ .../src/Rule/UserPasswordMatchesRule.php | 24 ++ .../Video/config/service_manager.php | 94 ++++++ .../src/Analyzer/VideoDurationAnalyzer.php | 32 ++ .../Video/src/Analyzer/VideoTitleAnalyzer.php | 66 ++++ .../Video/src/Builder/VideoBuilder.php | 21 ++ .../Video/src/Builder/VideoEventBuilder.php | 29 ++ .../Video/src/Builder/VideoImageBuilder.php | 55 +++ .../Video/src/ConfigProvider.php | 15 + .../Video/src/Enum/VideoEventType.php | 8 + .../Exception/VideoAlreadyExistsException.php | 25 ++ .../Exception/VideoNotFoundByIdException.php | 25 ++ .../Handler/Command/Delete/DeleteCommand.php | 20 ++ .../Command/Delete/DeleteCommandBuilder.php | 18 + .../Command/Delete/DeleteCommandHandler.php | 63 ++++ .../Command/Delete/DeleteCommandResult.php | 13 + .../SetThumbnail/SetThumbnailCommand.php | 26 ++ .../SetThumbnailCommandBuilder.php | 20 ++ .../SetThumbnailCommandHandler.php | 58 ++++ .../SetThumbnailCommandResult.php | 13 + .../UpdateDetails/UpdateDetailsCommand.php | 32 ++ .../UpdateDetailsCommandBuilder.php | 22 ++ .../UpdateDetailsCommandHandler.php | 78 +++++ .../UpdateDetailsCommandResult.php | 13 + .../Handler/Command/Upload/UploadCommand.php | 20 ++ .../Command/Upload/UploadCommandBuilder.php | 18 + .../Command/Upload/UploadCommandHandler.php | 33 ++ .../src/Handler/Query/Delete/DeleteQuery.php | 13 + .../Query/Delete/DeleteQueryBuilder.php | 16 + .../Query/Delete/DeleteQueryHandler.php | 18 + .../Query/Delete/DeleteQueryResult.php | 13 + .../Query/ReadDetails/ReadDetailsQuery.php | 20 ++ .../ReadDetails/ReadDetailsQueryBuilder.php | 18 + .../ReadDetails/ReadDetailsQueryHandler.php | 47 +++ .../ReadDetails/ReadDetailsQueryResult.php | 20 ++ .../src/Handler/Query/Stream/StreamQuery.php | 20 ++ .../Query/Stream/StreamQueryBuilder.php | 18 + .../Query/Stream/StreamQueryHandler.php | 38 +++ .../Query/Thumbnail/ThumbnailQuery.php | 20 ++ .../Query/Thumbnail/ThumbnailQueryBuilder.php | 18 + .../Query/Thumbnail/ThumbnailQueryHandler.php | 38 +++ .../Query/Thumbnail/ThumbnailQueryResult.php | 20 ++ .../Pipeline/Stream/Step/CreateEventStep.php | 59 ++++ .../Pipeline/Stream/Step/LoadVideoStep.php | 50 +++ .../Pipeline/Stream/Step/SaveEntitiesStep.php | 33 ++ .../src/Pipeline/Stream/StreamPayload.php | 43 +++ .../src/Pipeline/Stream/StreamPipeline.php | 25 ++ .../Upload/Step/AnalyzeDurationStep.php | 33 ++ .../Pipeline/Upload/Step/AnalyzeTitleStep.php | 33 ++ .../Pipeline/Upload/Step/BuildVideoStep.php | 36 ++ .../Pipeline/Upload/Step/CheckVideoStep.php | 40 +++ .../Pipeline/Upload/Step/SaveEntitiesStep.php | 31 ++ .../Pipeline/Upload/Step/UploadFileStep.php | 42 +++ .../src/Pipeline/Upload/UploadPayload.php | 32 ++ .../src/Pipeline/Upload/UploadPipeline.php | 46 +++ .../Video/src/Rule/VideoExistsRule.php | 29 ++ .../Video/src/Uploader/VideoUploader.php | 34 ++ .../VideoList/config/service_manager.php | 21 ++ .../VideoList/src/ConfigProvider.php | 15 + .../Handler/Command/Upload/UploadCommand.php | 18 + .../Command/Upload/UploadCommandBuilder.php | 16 + .../Command/Upload/UploadCommandHandler.php | 45 +++ .../Command/Upload/UploadCommandResult.php | 21 ++ .../Handler/Query/ReadList/ReadListQuery.php | 36 ++ .../Query/ReadList/ReadListQueryBuilder.php | 22 ++ .../Query/ReadList/ReadListQueryHandler.php | 34 ++ .../Query/ReadList/ReadListQueryResult.php | 20 ++ .../src/Model/UploadedFileResult.php | 66 ++++ .../Database/config/service_manager.php | 11 + .../Database/src/ConfigProvider.php | 15 + .../Database/src/ConfigServiceFactory.php | 33 ++ .../config/service_manager.php | 12 + .../src/ConfigProvider.php | 15 + .../src/ConfigServiceFactory.php | 21 ++ .../Encryption/config/service_manager.php | 12 + .../src/Client/EncryptionClient.php | 26 ++ .../Encryption/src/ConfigProvider.php | 15 + .../Exception/config/service_manager.php | 12 + .../Exception/src/ConfigProvider.php | 15 + .../Exception/src/ErrorCode.php | 12 + .../Exception/src/ErrorDomain.php | 16 + .../src/Exception/MyTubeException.php | 37 +++ .../MyTubeExceptionHandlerMiddleware.php | 48 +++ .../Logging/config/service_manager.php | 23 ++ .../Logging/src/ConfigProvider.php | 15 + .../src/Factory/FileStreamHandlerFactory.php | 29 ++ .../Logging/src/Factory/LoggerFactory.php | 22 ++ .../src/Factory/MonologLoggerFactory.php | 34 ++ .../src/Formatter/PrettyFileLogLines.php | 77 +++++ .../src/Handler/DoctrineLogHandler.php | 37 +++ .../Logging/src/Handler/FileStreamHandler.php | 21 ++ .../Logging/src/Logger/Logger.php | 91 +++++ .../Rbac/config/service_manager.php | 12 + .../Rbac/src/ConfigProvider.php | 15 + .../EnsureAuthorizationMiddleware.php | 70 ++++ .../Request/config/service_manager.php | 22 ++ .../Request/src/ConfigProvider.php | 15 + .../ApiIdentifierUnknownException.php | 22 ++ .../ApiPropertyUndefinedException.php | 24 ++ .../Exception/ApiRequestFailedException.php | 26 ++ .../Exception/ApiServiceUnknownException.php | 20 ++ .../src/Factory/RequestServiceFactory.php | 28 ++ .../src/Middleware/AnalyzeBodyMiddleware.php | 43 +++ .../Middleware/AnalyzeHeaderMiddleware.php | 37 +++ .../Middleware/InternalRequestMiddleware.php | 47 +++ .../Request/src/Service/RequestService.php | 95 ++++++ .../Response/src/ErrorResponse.php | 37 +++ .../Response/src/ForbiddenResponse.php | 16 + .../Response/src/SuccessResponse.php | 17 + .../Response/src/UnauthorizedResponse.php | 16 + .../Session/config/service_manager.php | 14 + .../Session/src/ConfigProvider.php | 15 + .../src/Middleware/LoggedInUserMiddleware.php | 41 +++ .../src/Middleware/SessionMiddleware.php | 81 +++++ .../UuidGenerator/src/UuidGenerator.php | 18 + 352 files changed, 11695 insertions(+) create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .github/workflows/create branch on issue.yml create mode 100644 .github/workflows/php.yml create mode 100644 .gitignore create mode 100644 .htaccess create mode 100644 COPYRIGHT.md create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 bin/clear-config-cache.php create mode 100644 bin/console.php create mode 100644 bin/createApi.php create mode 100644 bin/createPipeline.php create mode 100644 bin/doctrine-migrations-log.php create mode 100644 bin/doctrine-migrations.php create mode 100755 bin/script/build create mode 100755 bin/script/down create mode 100755 bin/script/exec create mode 100755 bin/script/init create mode 100755 bin/script/stop create mode 100755 bin/script/up create mode 100644 composer.development.json create mode 100644 composer.json create mode 100644 config/autoload/.gitignore create mode 100644 config/autoload/api.global.php create mode 100644 config/autoload/authorization.global.php create mode 100644 config/autoload/defines.php create mode 100644 config/autoload/dependencies.global.php create mode 100644 config/autoload/doctrine.global.php create mode 100644 config/autoload/logger.global.php create mode 100644 config/autoload/mezzio.global.php create mode 100644 config/config.php create mode 100644 config/container.php create mode 100644 config/development.config.php create mode 100644 config/pipeline.php create mode 100644 config/routes.php create mode 100644 data/migrations/log/Version20230922150649.php create mode 100644 data/migrations/myTube/Version20230922085011.php create mode 100644 data/migrations/myTube/Version20230922092351.php create mode 100644 data/migrations/myTube/Version20230922092754.php create mode 100644 data/migrations/myTube/Version20230922101354.php create mode 100644 data/migrations/myTube/Version20230922101355.php create mode 100644 data/migrations/myTube/Version20230924113403.php create mode 100644 data/migrations/myTube/Version20240214194156.php create mode 100644 data/migrations/myTube/Version20240223130626.php create mode 100644 data/migrations/myTube/Version20240223130835.php create mode 100644 data/migrations/myTube/Version20240223142111.php create mode 100644 data/migrations/myTube/Version20240229205003.php create mode 100644 data/migrations/myTube/Version20240323095155.php create mode 100644 docker/docker-compose-mac.yml create mode 100644 docker/docker-compose.yml create mode 100644 docker/mysql/dockerfile create mode 100644 docker/mysql/scripts/initdb.sql create mode 100644 docker/nginx/config/nginx.conf create mode 100644 docker/nginx/config/upstream.conf create mode 100644 docker/nginx/dockerfile create mode 100644 docker/php/config/uploads.ini create mode 100644 docker/php/dockerfile create mode 100644 phpunit.xml.dist create mode 100644 public/.htaccess create mode 100644 public/index.php create mode 100644 src/ApiDomain/Console/config/console.php create mode 100644 src/ApiDomain/Console/config/service_manager.php create mode 100644 src/ApiDomain/Console/src/Command/AnalyzeVideoTitlesCommand.php create mode 100644 src/ApiDomain/Console/src/Command/InitializeDataCommand.php create mode 100644 src/ApiDomain/Console/src/Command/RbacUpdateCommand.php create mode 100644 src/ApiDomain/Console/src/Command/ReadUntaggedVideosCommand.php create mode 100644 src/ApiDomain/Console/src/ConfigProvider.php create mode 100644 src/ApiDomain/External/Authentication/config/routes.php create mode 100644 src/ApiDomain/External/Authentication/config/service_manager.php create mode 100644 src/ApiDomain/External/Authentication/src/ConfigProvider.php create mode 100644 src/ApiDomain/External/Authentication/src/Handler/ConfirmRegistrationHandler.php create mode 100644 src/ApiDomain/External/Authentication/src/Handler/LoginUserHandler.php create mode 100644 src/ApiDomain/External/Authentication/src/Handler/LogoutUserHandler.php create mode 100644 src/ApiDomain/External/Authentication/src/Handler/RegisterUserHandler.php create mode 100644 src/ApiDomain/External/Health/config/routes.php create mode 100644 src/ApiDomain/External/Health/config/service_manager.php create mode 100644 src/ApiDomain/External/Health/src/ConfigProvider.php create mode 100644 src/ApiDomain/External/Health/src/Handler/HealthHandler.php create mode 100644 src/ApiDomain/External/Tag/config/routes.php create mode 100644 src/ApiDomain/External/Tag/config/service_manager.php create mode 100644 src/ApiDomain/External/Tag/src/ConfigProvider.php create mode 100644 src/ApiDomain/External/Tag/src/Handler/AddAliasHandler.php create mode 100644 src/ApiDomain/External/Tag/src/Handler/CreateHandler.php create mode 100644 src/ApiDomain/External/Tag/src/Handler/ReadDetailsHandler.php create mode 100644 src/ApiDomain/External/Tag/src/Handler/ReadVideoListHandler.php create mode 100644 src/ApiDomain/External/Tag/src/ResponseFormatter/AddAliasResponseFormatter.php create mode 100644 src/ApiDomain/External/Tag/src/ResponseFormatter/CreateResponseFormatter.php create mode 100644 src/ApiDomain/External/Tag/src/ResponseFormatter/ReadDetailsResponseFormatter.php create mode 100644 src/ApiDomain/External/Tag/src/ResponseFormatter/ReadVideoListResponseFormatter.php create mode 100644 src/ApiDomain/External/TagList/config/routes.php create mode 100644 src/ApiDomain/External/TagList/config/service_manager.php create mode 100644 src/ApiDomain/External/TagList/src/ConfigProvider.php create mode 100644 src/ApiDomain/External/TagList/src/Handler/ReadListHandler.php create mode 100644 src/ApiDomain/External/TagList/src/ResponseFormatter/ReadListResponseFormatter.php create mode 100644 src/ApiDomain/External/User/config/routes.php create mode 100644 src/ApiDomain/External/User/config/service_manager.php create mode 100644 src/ApiDomain/External/User/src/ConfigProvider.php create mode 100644 src/ApiDomain/External/User/src/Formatter/UserFormatter.php create mode 100644 src/ApiDomain/External/User/src/Handler/ChangePasswordHandler.php create mode 100644 src/ApiDomain/External/User/src/Handler/ChangeUsernameHandler.php create mode 100644 src/ApiDomain/External/User/src/Handler/CreateUserHandler.php create mode 100644 src/ApiDomain/External/User/src/Handler/UserStateHandler.php create mode 100644 src/ApiDomain/External/Video/config/routes.php create mode 100644 src/ApiDomain/External/Video/config/service_manager.php create mode 100644 src/ApiDomain/External/Video/src/ConfigProvider.php create mode 100644 src/ApiDomain/External/Video/src/Handler/DeleteHandler.php create mode 100644 src/ApiDomain/External/Video/src/Handler/ReadDetailsHandler.php create mode 100644 src/ApiDomain/External/Video/src/Handler/SetThumbnailHandler.php create mode 100644 src/ApiDomain/External/Video/src/Handler/StreamHandler.php create mode 100644 src/ApiDomain/External/Video/src/Handler/ThumbnailHandler.php create mode 100644 src/ApiDomain/External/Video/src/Handler/UpdateDetailsHandler.php create mode 100644 src/ApiDomain/External/Video/src/Handler/UploadHandler.php create mode 100644 src/ApiDomain/External/Video/src/ResponseFormatter/DeleteResponseFormatter.php create mode 100644 src/ApiDomain/External/Video/src/ResponseFormatter/ReadDetailsResponseFormatter.php create mode 100644 src/ApiDomain/External/Video/src/ResponseFormatter/SetThumbnailResponseFormatter.php create mode 100644 src/ApiDomain/External/Video/src/ResponseFormatter/ThumbnailResponseFormatter.php create mode 100644 src/ApiDomain/External/Video/src/ResponseFormatter/UpdateDetailsResponseFormatter.php create mode 100644 src/ApiDomain/External/VideoList/config/routes.php create mode 100644 src/ApiDomain/External/VideoList/config/service_manager.php create mode 100644 src/ApiDomain/External/VideoList/src/ConfigProvider.php create mode 100644 src/ApiDomain/External/VideoList/src/Handler/ReadListHandler.php create mode 100644 src/ApiDomain/External/VideoList/src/Handler/UploadHandler.php create mode 100644 src/ApiDomain/External/VideoList/src/ResponseFormatter/ReadListResponseFormatter.php create mode 100644 src/ApiDomain/External/VideoList/src/ResponseFormatter/UploadResponseFormatter.php create mode 100644 src/DataDomain/Business/config/doctrine.php create mode 100644 src/DataDomain/Business/config/service_manager.php create mode 100644 src/DataDomain/Business/src/ConfigProvider.php create mode 100644 src/DataDomain/Business/src/Entity/Permission.php create mode 100644 src/DataDomain/Business/src/Entity/Registration.php create mode 100644 src/DataDomain/Business/src/Entity/Role.php create mode 100644 src/DataDomain/Business/src/Entity/Tag.php create mode 100644 src/DataDomain/Business/src/Entity/TagAlias.php create mode 100644 src/DataDomain/Business/src/Entity/User.php create mode 100644 src/DataDomain/Business/src/Entity/UserSession.php create mode 100644 src/DataDomain/Business/src/Entity/Video.php create mode 100644 src/DataDomain/Business/src/Entity/VideoEvent.php create mode 100644 src/DataDomain/Business/src/Factory/MyTubeEntityManagerFactory.php create mode 100644 src/DataDomain/Business/src/Manager/MyTubeEntityManager.php create mode 100644 src/DataDomain/Business/src/Repository/PermissionRepository.php create mode 100644 src/DataDomain/Business/src/Repository/ProductRepository.php create mode 100644 src/DataDomain/Business/src/Repository/RegistrationRepository.php create mode 100644 src/DataDomain/Business/src/Repository/RoleRepository.php create mode 100644 src/DataDomain/Business/src/Repository/TagAliasRepository.php create mode 100644 src/DataDomain/Business/src/Repository/TagRepository.php create mode 100644 src/DataDomain/Business/src/Repository/UserRepository.php create mode 100644 src/DataDomain/Business/src/Repository/UserSessionRepository.php create mode 100644 src/DataDomain/Business/src/Repository/VideoEventRepository.php create mode 100644 src/DataDomain/Business/src/Repository/VideoRepository.php create mode 100644 src/DataDomain/Log/config/doctrine.php create mode 100644 src/DataDomain/Log/config/service_manager.php create mode 100644 src/DataDomain/Log/src/ConfigProvider.php create mode 100644 src/DataDomain/Log/src/Entity/Log.php create mode 100644 src/DataDomain/Log/src/Factory/LogEntityManagerFactory.php create mode 100644 src/DataDomain/Log/src/Manager/LogEntityManager.php create mode 100644 src/DataDomain/Log/src/Repository/LogRepository.php create mode 100644 src/HandlingDomain/Registration/config/service_manager.php create mode 100644 src/HandlingDomain/Registration/src/Builder/RegistrationBuilder.php create mode 100644 src/HandlingDomain/Registration/src/ConfigProvider.php create mode 100644 src/HandlingDomain/Registration/src/Exception/RegistrationNotFoundByIdException.php create mode 100644 src/HandlingDomain/Registration/src/Exception/RegistrationWithIdentifierAlreadyExistsException.php create mode 100644 src/HandlingDomain/Registration/src/Handler/Command/ConfirmRegistration/ConfirmRegistrationCommand.php create mode 100644 src/HandlingDomain/Registration/src/Handler/Command/ConfirmRegistration/ConfirmRegistrationCommandBuilder.php create mode 100644 src/HandlingDomain/Registration/src/Handler/Command/ConfirmRegistration/ConfirmRegistrationCommandHandler.php create mode 100644 src/HandlingDomain/Registration/src/Handler/Command/RegisterUser/RegisterUserCommand.php create mode 100644 src/HandlingDomain/Registration/src/Handler/Command/RegisterUser/RegisterUserCommandBuilder.php create mode 100644 src/HandlingDomain/Registration/src/Handler/Command/RegisterUser/RegisterUserCommandHandler.php create mode 100644 src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/ConfirmRegistrationPayload.php create mode 100644 src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/ConfirmRegistrationPipeline.php create mode 100644 src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/Step/CreateUserStep.php create mode 100644 src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/Step/LoadRegistrationStep.php create mode 100644 src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/Step/SaveRegistrationAndUserStep.php create mode 100644 src/HandlingDomain/Registration/src/Pipeline/RegisterUser/RegisterUserPayload.php create mode 100644 src/HandlingDomain/Registration/src/Pipeline/RegisterUser/RegisterUserPipeline.php create mode 100644 src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/BuildRegistrationStep.php create mode 100644 src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/CheckIdentifierStep.php create mode 100644 src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/SaveRegistrationStep.php create mode 100644 src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/SendMailStep.php create mode 100644 src/HandlingDomain/Registration/src/Rule/RegistrationWithIdentifierAlreadyExistsRule.php create mode 100644 src/HandlingDomain/Role/src/Exception/RoleNotFoundByIdentifierException.php create mode 100644 src/HandlingDomain/Tag/config/service_manager.php create mode 100644 src/HandlingDomain/Tag/src/Builder/TagAliasBuilder.php create mode 100644 src/HandlingDomain/Tag/src/Builder/TagBuilder.php create mode 100644 src/HandlingDomain/Tag/src/ConfigProvider.php create mode 100644 src/HandlingDomain/Tag/src/Exception/TagAliasAlreadyExistsException.php create mode 100644 src/HandlingDomain/Tag/src/Exception/TagAlreadyExistsException.php create mode 100644 src/HandlingDomain/Tag/src/Exception/TagNotFoundByIdException.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommand.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommandBuilder.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommandHandler.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommandResult.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommand.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommandBuilder.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommandHandler.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommandResult.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQuery.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQueryBuilder.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQueryHandler.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQueryResult.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQuery.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQueryBuilder.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQueryHandler.php create mode 100644 src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQueryResult.php create mode 100644 src/HandlingDomain/Tag/src/Rule/TagAliasExistsRule.php create mode 100644 src/HandlingDomain/Tag/src/Rule/TagExistsRule.php create mode 100644 src/HandlingDomain/TagList/config/service_manager.php create mode 100644 src/HandlingDomain/TagList/src/ConfigProvider.php create mode 100644 src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQuery.php create mode 100644 src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQueryBuilder.php create mode 100644 src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQueryHandler.php create mode 100644 src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQueryResult.php create mode 100644 src/HandlingDomain/User/config/service_manager.php create mode 100644 src/HandlingDomain/User/src/Builder/UserBuilder.php create mode 100644 src/HandlingDomain/User/src/ConfigProvider.php create mode 100644 src/HandlingDomain/User/src/Exception/UserNotFoundByIdentifierException.php create mode 100644 src/HandlingDomain/User/src/Exception/UserPasswordMismatchException.php create mode 100644 src/HandlingDomain/User/src/Exception/UserWithIdentifierAlreadyExists.php create mode 100644 src/HandlingDomain/User/src/Exception/UserWrongPasswordException.php create mode 100644 src/HandlingDomain/User/src/Handler/Command/ChangePassword/ChangePasswordCommand.php create mode 100644 src/HandlingDomain/User/src/Handler/Command/ChangePassword/ChangePasswordCommandBuilder.php create mode 100644 src/HandlingDomain/User/src/Handler/Command/ChangePassword/ChangePasswordCommandHandler.php create mode 100644 src/HandlingDomain/User/src/Handler/Command/ChangeUsername/ChangeUsernameCommand.php create mode 100644 src/HandlingDomain/User/src/Handler/Command/ChangeUsername/ChangeUsernameCommandBuilder.php create mode 100644 src/HandlingDomain/User/src/Handler/Command/ChangeUsername/ChangeUsernameCommandHandler.php create mode 100644 src/HandlingDomain/User/src/Handler/Command/CreateUser/CreateUserCommand.php create mode 100644 src/HandlingDomain/User/src/Handler/Command/CreateUser/CreateUserCommandBuilder.php create mode 100644 src/HandlingDomain/User/src/Handler/Command/CreateUser/CreateUserCommandHandler.php create mode 100644 src/HandlingDomain/User/src/Rule/UserPasswordMatchRule.php create mode 100644 src/HandlingDomain/User/src/Rule/UserWithIdentifierAlreadyExistsRule.php create mode 100644 src/HandlingDomain/UserSession/config/service_manager.php create mode 100644 src/HandlingDomain/UserSession/src/Builder/UserSessionBuilder.php create mode 100644 src/HandlingDomain/UserSession/src/ConfigProvider.php create mode 100644 src/HandlingDomain/UserSession/src/Handler/Command/LoginUser/LoginUserCommand.php create mode 100644 src/HandlingDomain/UserSession/src/Handler/Command/LoginUser/LoginUserCommandBuilder.php create mode 100644 src/HandlingDomain/UserSession/src/Handler/Command/LoginUser/LoginUserCommandHandler.php create mode 100644 src/HandlingDomain/UserSession/src/Handler/Command/LogoutUser/LogoutUserCommand.php create mode 100644 src/HandlingDomain/UserSession/src/Handler/Command/LogoutUser/LogoutUserCommandBuilder.php create mode 100644 src/HandlingDomain/UserSession/src/Handler/Command/LogoutUser/LogoutUserCommandHandler.php create mode 100644 src/HandlingDomain/UserSession/src/Rule/UserPasswordMatchesRule.php create mode 100644 src/HandlingDomain/Video/config/service_manager.php create mode 100644 src/HandlingDomain/Video/src/Analyzer/VideoDurationAnalyzer.php create mode 100644 src/HandlingDomain/Video/src/Analyzer/VideoTitleAnalyzer.php create mode 100644 src/HandlingDomain/Video/src/Builder/VideoBuilder.php create mode 100644 src/HandlingDomain/Video/src/Builder/VideoEventBuilder.php create mode 100644 src/HandlingDomain/Video/src/Builder/VideoImageBuilder.php create mode 100644 src/HandlingDomain/Video/src/ConfigProvider.php create mode 100644 src/HandlingDomain/Video/src/Enum/VideoEventType.php create mode 100644 src/HandlingDomain/Video/src/Exception/VideoAlreadyExistsException.php create mode 100644 src/HandlingDomain/Video/src/Exception/VideoNotFoundByIdException.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommand.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommandBuilder.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommandHandler.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommandResult.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/SetThumbnail/SetThumbnailCommand.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/SetThumbnail/SetThumbnailCommandBuilder.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/SetThumbnail/SetThumbnailCommandHandler.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/SetThumbnail/SetThumbnailCommandResult.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/UpdateDetails/UpdateDetailsCommand.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/UpdateDetails/UpdateDetailsCommandBuilder.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/UpdateDetails/UpdateDetailsCommandHandler.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/UpdateDetails/UpdateDetailsCommandResult.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/Upload/UploadCommand.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/Upload/UploadCommandBuilder.php create mode 100644 src/HandlingDomain/Video/src/Handler/Command/Upload/UploadCommandHandler.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/Delete/DeleteQuery.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/Delete/DeleteQueryBuilder.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/Delete/DeleteQueryHandler.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/Delete/DeleteQueryResult.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/ReadDetails/ReadDetailsQuery.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/ReadDetails/ReadDetailsQueryBuilder.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/ReadDetails/ReadDetailsQueryHandler.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/ReadDetails/ReadDetailsQueryResult.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/Stream/StreamQuery.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/Stream/StreamQueryBuilder.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/Stream/StreamQueryHandler.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQuery.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQueryBuilder.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQueryHandler.php create mode 100644 src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQueryResult.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Stream/Step/CreateEventStep.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Stream/Step/LoadVideoStep.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Stream/Step/SaveEntitiesStep.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Stream/StreamPayload.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Stream/StreamPipeline.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Upload/Step/AnalyzeDurationStep.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Upload/Step/AnalyzeTitleStep.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Upload/Step/BuildVideoStep.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Upload/Step/CheckVideoStep.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Upload/Step/SaveEntitiesStep.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Upload/Step/UploadFileStep.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Upload/UploadPayload.php create mode 100644 src/HandlingDomain/Video/src/Pipeline/Upload/UploadPipeline.php create mode 100644 src/HandlingDomain/Video/src/Rule/VideoExistsRule.php create mode 100644 src/HandlingDomain/Video/src/Uploader/VideoUploader.php create mode 100644 src/HandlingDomain/VideoList/config/service_manager.php create mode 100644 src/HandlingDomain/VideoList/src/ConfigProvider.php create mode 100644 src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommand.php create mode 100644 src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommandBuilder.php create mode 100644 src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommandHandler.php create mode 100644 src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommandResult.php create mode 100644 src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQuery.php create mode 100644 src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQueryBuilder.php create mode 100644 src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQueryHandler.php create mode 100644 src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQueryResult.php create mode 100644 src/HandlingDomain/VideoList/src/Model/UploadedFileResult.php create mode 100644 src/Infrastructure/Database/config/service_manager.php create mode 100644 src/Infrastructure/Database/src/ConfigProvider.php create mode 100644 src/Infrastructure/Database/src/ConfigServiceFactory.php create mode 100644 src/Infrastructure/DependencyInjection/config/service_manager.php create mode 100644 src/Infrastructure/DependencyInjection/src/ConfigProvider.php create mode 100644 src/Infrastructure/DependencyInjection/src/ConfigServiceFactory.php create mode 100644 src/Infrastructure/Encryption/config/service_manager.php create mode 100644 src/Infrastructure/Encryption/src/Client/EncryptionClient.php create mode 100644 src/Infrastructure/Encryption/src/ConfigProvider.php create mode 100644 src/Infrastructure/Exception/config/service_manager.php create mode 100644 src/Infrastructure/Exception/src/ConfigProvider.php create mode 100644 src/Infrastructure/Exception/src/ErrorCode.php create mode 100644 src/Infrastructure/Exception/src/ErrorDomain.php create mode 100644 src/Infrastructure/Exception/src/Exception/MyTubeException.php create mode 100644 src/Infrastructure/Exception/src/Middleware/MyTubeExceptionHandlerMiddleware.php create mode 100644 src/Infrastructure/Logging/config/service_manager.php create mode 100644 src/Infrastructure/Logging/src/ConfigProvider.php create mode 100644 src/Infrastructure/Logging/src/Factory/FileStreamHandlerFactory.php create mode 100644 src/Infrastructure/Logging/src/Factory/LoggerFactory.php create mode 100644 src/Infrastructure/Logging/src/Factory/MonologLoggerFactory.php create mode 100644 src/Infrastructure/Logging/src/Formatter/PrettyFileLogLines.php create mode 100644 src/Infrastructure/Logging/src/Handler/DoctrineLogHandler.php create mode 100644 src/Infrastructure/Logging/src/Handler/FileStreamHandler.php create mode 100644 src/Infrastructure/Logging/src/Logger/Logger.php create mode 100644 src/Infrastructure/Rbac/config/service_manager.php create mode 100644 src/Infrastructure/Rbac/src/ConfigProvider.php create mode 100644 src/Infrastructure/Rbac/src/Middleware/EnsureAuthorizationMiddleware.php create mode 100644 src/Infrastructure/Request/config/service_manager.php create mode 100644 src/Infrastructure/Request/src/ConfigProvider.php create mode 100644 src/Infrastructure/Request/src/Exception/ApiIdentifierUnknownException.php create mode 100644 src/Infrastructure/Request/src/Exception/ApiPropertyUndefinedException.php create mode 100644 src/Infrastructure/Request/src/Exception/ApiRequestFailedException.php create mode 100644 src/Infrastructure/Request/src/Exception/ApiServiceUnknownException.php create mode 100644 src/Infrastructure/Request/src/Factory/RequestServiceFactory.php create mode 100644 src/Infrastructure/Request/src/Middleware/AnalyzeBodyMiddleware.php create mode 100644 src/Infrastructure/Request/src/Middleware/AnalyzeHeaderMiddleware.php create mode 100644 src/Infrastructure/Request/src/Middleware/InternalRequestMiddleware.php create mode 100644 src/Infrastructure/Request/src/Service/RequestService.php create mode 100644 src/Infrastructure/Response/src/ErrorResponse.php create mode 100644 src/Infrastructure/Response/src/ForbiddenResponse.php create mode 100644 src/Infrastructure/Response/src/SuccessResponse.php create mode 100644 src/Infrastructure/Response/src/UnauthorizedResponse.php create mode 100644 src/Infrastructure/Session/config/service_manager.php create mode 100644 src/Infrastructure/Session/src/ConfigProvider.php create mode 100644 src/Infrastructure/Session/src/Middleware/LoggedInUserMiddleware.php create mode 100644 src/Infrastructure/Session/src/Middleware/SessionMiddleware.php create mode 100644 src/Infrastructure/UuidGenerator/src/UuidGenerator.php diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..bf42cae --- /dev/null +++ b/.env.example @@ -0,0 +1,13 @@ +# DB Configuration +DB_DRIVER=pdo_mysql +DB_HOST=myTube-backend-mysql +DB_PORT=3306 +DB_USER=myTube +DB_PASSWORD=pass +DB_NAME=myTube +DB_NAME_LOG=log + +# MyTube Setup +INIT_USER_NAME=admin +INIT_USER_PASSWORD=password +INIT_USER_MAIL=admin@test.com \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..88df8ad --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +/.gitattributes export-ignore +/.github/ export-ignore +/.laminas-ci.json export-ignore +/phpcs.xml.dist export-ignore +/psalm.xml.dist export-ignore +/psalm-baseline.xml export-ignore +/renovate.json export-ignore diff --git a/.github/workflows/create branch on issue.yml b/.github/workflows/create branch on issue.yml new file mode 100644 index 0000000..789cd5a --- /dev/null +++ b/.github/workflows/create branch on issue.yml @@ -0,0 +1,40 @@ +name: Create Branch on Issue Transition to In Progress + +on: + issues: + types: + - edited + +jobs: + create-branch: + if: github.event.changes.issue.labels + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Get issue details + run: | + issue_title=$(jq -r ".issue.number" "$GITHUB_EVENT_PATH") + previous_labels=$(jq -r ".changes.labels.from" "$GITHUB_EVENT_PATH") + current_labels=$(jq -r ".issue.labels | map(.name) | join(\", \")" "$GITHUB_EVENT_PATH") + branch_name="" + + # Check if the issue transitioned from 'Todo' to 'In Progress' + if [[ "$previous_labels" == *"Todo"* && "$current_labels" == *"In Progress"* ]]; then + project_name="hp-be" # Verwende "hp-be" als Projektnamen + branch_name="${project_name}-${issue_title}" + echo "Branch name: $branch_name" + fi + + echo "::set-output name=branch_name::$branch_name" + shell: bash + + - name: Create and switch to new branch + run: | + branch_name="${{ steps.create-branch.outputs.branch_name }}" + if [ -n "$branch_name" ]; then + git checkout -b "$branch_name" + git push origin "$branch_name" + fi + working-directory: ${{ github.workspace }} diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml new file mode 100644 index 0000000..1ef7894 --- /dev/null +++ b/.github/workflows/php.yml @@ -0,0 +1,39 @@ +name: PHP Composer + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit" + # Docs: https://getcomposer.org/doc/articles/scripts.md + + # - name: Run test suite + # run: composer run-script test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..810eee8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +/.phpcs-cache +/.phpunit.result.cache +/.idea +/clover.xml +/coveralls-upload.json +/phpunit.xml + +/data/cache/ +/data/db/ +/public/data/ +/var/ +/vendor/ + +*.env +composer.lock diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..562d1fb --- /dev/null +++ b/.htaccess @@ -0,0 +1,3 @@ +RewriteEngine On +RewriteBase / +RewriteRule ^pages/([^/]*)/(.*)$ pages.php?$1=$2 diff --git a/COPYRIGHT.md b/COPYRIGHT.md new file mode 100644 index 0000000..0a8cccc --- /dev/null +++ b/COPYRIGHT.md @@ -0,0 +1 @@ +Copyright (c) 2020 Laminas Project a Series of LF Projects, LLC. (https://getlaminas.org/) diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..10b40f1 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,26 @@ +Copyright (c) 2020 Laminas Project a Series of LF Projects, LLC. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +- Neither the name of Laminas Foundation nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/bin/clear-config-cache.php b/bin/clear-config-cache.php new file mode 100644 index 0000000..b1c27ca --- /dev/null +++ b/bin/clear-config-cache.php @@ -0,0 +1,39 @@ +get('config'); + $commands = $config['console']['commands']; + + $app = new Application(); + foreach ($commands as $command) { + $app->add($container->get($command)); + } + + try { + $app->setCatchExceptions(false); + $app->run(); + } catch (Throwable $e) { + $logger = new Logger(); + $logger->error( + $e->getMessage(), + ['exception' => $e] + ); + } +}); \ No newline at end of file diff --git a/bin/createApi.php b/bin/createApi.php new file mode 100644 index 0000000..5749bc1 --- /dev/null +++ b/bin/createApi.php @@ -0,0 +1,312 @@ + 'TODO.TODO', + 'path' => '/api/TODO/TODO[/]', + 'allowed_methods' => ['POST'], + 'middleware' => [ + {$apiHandlerName}::class, + ], + ], +]; +"; + $routesFilePath = $apiDirectoryPath . 'config/routes.php'; + writeToFile($routesFilePath, $routesFileContent); + + $serviceManagerFileContent = " [ + // Handler + {$apiHandlerName}::class => AutoWiringFactory::class, + + // Response Formatter + {$apiResponseFormatterName}::class => AutoWiringFactory::class, + ], +]; +"; + $serviceManagerFilePath = $apiDirectoryPath . 'config/service_manager.php'; + writeToFile($serviceManagerFilePath, $serviceManagerFileContent); + + $configProviderFileContent = " require __DIR__ . './../config/service_manager.php', + 'routes' => require __DIR__ . '/./../config/routes.php', + ]; + } +} +"; + $configProviderFilePath = $apiDirectoryPath . 'src/ConfigProvider.php'; + writeToFile($configProviderFilePath, $configProviderFileContent); +} + +if (!file_exists($cqrsDirectoryPath)) { + $serviceManagerFileContent = " [ + /// CQRS + // {$apiName} + {$cqrsBuilderName}::class => AutoWiringFactory::class, + {$cqrsHandlerName}::class => AutoWiringFactory::class, + ], +]; +"; + $serviceManagerFilePath = $cqrsDirectoryPath . 'config/service_manager.php'; + writeToFile($serviceManagerFilePath, $serviceManagerFileContent); + + $configProviderFileContent = " require __DIR__ . './../config/service_manager.php', + ]; + } +} +"; + $configProviderFilePath = $cqrsDirectoryPath . 'src/ConfigProvider.php'; + writeToFile($configProviderFilePath, $configProviderFileContent); + +} + +$apiHandlerFileContent = "getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + \${$cqrsVariableName} = \$this->{$cqrsBuilderVariableName}->build( + \$data + ); + \$result = \$this->{$cqrsHandlerVariableName}->execute(\${$cqrsVariableName}); + + return new SuccessResponse(\$this->responseFormatter->format(\$result)); + } +} +"; +writeToFile($apiHandlerFilePath, $apiHandlerFileContent); + +$apiResponseFormatterFileContent = " $stepClassName, + 'stepVariableName' => lcfirst($stepClassName), + 'stepFilePath' => $stepsFilePath . $stepClassName . '.php', + 'stepUsingNamespace' => 'use ' . $payloadFullNamespace . '\\Step\\' . $stepClassName, + ]; +} + + + + +########## WRITE FILES ############### +function writeToFile($path, $content) { + echo 'Writing contents to file ' . $path . PHP_EOL; + + $directory = pathinfo($path, PATHINFO_DIRNAME); + if (!is_dir($directory)) { + mkdir($directory, 0755, true); + } + + file_put_contents($path, $content); +} + +$createNamespaceFiles = !file_exists($pipelineDirectoryPath); + +$stepsUsingNamespaces = []; +$stepsDeclarations = []; +$stepsAutoWirings = []; +$stepsReferences = []; + +foreach ($steps as $step) { + $stepClassName = $step['stepClassName']; + $stepVariableName = $step['stepVariableName']; + $stepFilePath = $step['stepFilePath']; + $stepUsingNamespace = $step['stepUsingNamespace']; + + $stepsUsingNamespaces[] = $stepUsingNamespace . ';'; + $stepsDeclarations[] = 'private readonly ' . $stepClassName . ' $' . $stepVariableName . ','; + $stepsAutoWirings[] = $stepClassName . '::class => AutoWiringFactory::class,'; + $stepsReferences[] = '$this->' . $stepVariableName . ','; + + $stepFileContent = "next()(\$payload, \$pipeline); + } +} +"; + writeToFile($stepFilePath, $stepFileContent); +} + +$stepsUsingNamespace = implode(PHP_EOL, $stepsUsingNamespaces); +$stepsDeclaration = implode(PHP_EOL . ' ', $stepsDeclarations); +$stepsAutoWiring = implode(PHP_EOL . ' ', $stepsAutoWirings); +$stepsReference = implode(PHP_EOL . ' ', $stepsReferences); + + + +$pipelineFileContent = " [ + /// Pipeline + // {$pipelineName} + {$pipelineClassName}::class => AutoWiringFactory::class, + {$stepsAutoWiring} + ], +]; +"; + $serviceManagerFilePath = $pipelineDirectoryPath . 'config/service_manager.php'; + writeToFile($serviceManagerFilePath, $serviceManagerFileContent); + + $configProviderFileContent = " require __DIR__ . './../config/service_manager.php', + ]; + } +} +"; + $configProviderFilePath = $pipelineDirectoryPath . 'src/ConfigProvider.php'; + writeToFile($configProviderFilePath, $configProviderFileContent); + +} diff --git a/bin/doctrine-migrations-log.php b/bin/doctrine-migrations-log.php new file mode 100644 index 0000000..a93f3bc --- /dev/null +++ b/bin/doctrine-migrations-log.php @@ -0,0 +1,91 @@ +get('config'); +$doctrineConfig = $config['doctrine']; +$paths = $doctrineConfig['driver']['orm_log_annotation_driver']['paths']; + +$dbParams = $doctrineConfig['connection']['orm_log']['params']; +$migrationsConf = $doctrineConfig['migrations_configuration']['orm_log']; + +$reader = new AnnotationReader(); +$driver = new AnnotationDriver($reader, $paths); + +$config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode); +$config->setMetadataDriverImpl($driver); +$entityManager = $container->get(MyTubeEntityManager::class); + +try { + $connection = DriverManager::getConnection($dbParams); +} catch (\Doctrine\DBAL\Exception $e) { + echo $e->getMessage(); + exit; +} + +$configuration = new Configuration($connection); + +$configuration->addMigrationsDirectory( + $migrationsConf['namespace'], + $migrationsConf['directory'] +); +$configuration->setAllOrNothing(true); +$configuration->setCheckDatabasePlatform(false); + +$storageConfiguration = new TableMetadataStorageConfiguration(); +$storageConfiguration->setTableName($migrationsConf['table']); + +$configuration->setMetadataStorageConfiguration($storageConfiguration); + +$dependencyFactory = DependencyFactory::fromConnection( + new ExistingConfiguration($configuration), + new ExistingConnection($connection) +); + +$cli = new Application('Doctrine Migrations'); +$cli->setCatchExceptions(true); + +$cli->addCommands([ + new DumpSchemaCommand($dependencyFactory), + new ExecuteCommand($dependencyFactory), + new GenerateCommand($dependencyFactory), + new LatestCommand($dependencyFactory), + new ListCommand($dependencyFactory), + new MigrateCommand($dependencyFactory), + new RollupCommand($dependencyFactory), + new StatusCommand($dependencyFactory), + new SyncMetadataCommand($dependencyFactory), + new VersionCommand($dependencyFactory), +]); + +try { + $cli->run(); +} catch (Exception $e) { + echo $e->getMessage(); +} diff --git a/bin/doctrine-migrations.php b/bin/doctrine-migrations.php new file mode 100644 index 0000000..82317f1 --- /dev/null +++ b/bin/doctrine-migrations.php @@ -0,0 +1,91 @@ +get('config'); +$doctrineConfig = $config['doctrine']; +$paths = $doctrineConfig['driver']['orm_myTube_annotation_driver']['paths']; + +$dbParams = $doctrineConfig['connection']['orm_myTube']['params']; +$migrationsConf = $doctrineConfig['migrations_configuration']['orm_myTube']; + +$reader = new AnnotationReader(); +$driver = new AnnotationDriver($reader, $paths); + +$config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode); +$config->setMetadataDriverImpl($driver); +$entityManager = $container->get(MyTubeEntityManager::class); + +try { + $connection = DriverManager::getConnection($dbParams); +} catch (\Doctrine\DBAL\Exception $e) { + echo $e->getMessage(); + exit; +} + +$configuration = new Configuration($connection); + +$configuration->addMigrationsDirectory( + $migrationsConf['namespace'], + $migrationsConf['directory'] +); +$configuration->setAllOrNothing(true); +$configuration->setCheckDatabasePlatform(false); + +$storageConfiguration = new TableMetadataStorageConfiguration(); +$storageConfiguration->setTableName($migrationsConf['table']); + +$configuration->setMetadataStorageConfiguration($storageConfiguration); + +$dependencyFactory = DependencyFactory::fromConnection( + new ExistingConfiguration($configuration), + new ExistingConnection($connection) +); + +$cli = new Application('Doctrine Migrations'); +$cli->setCatchExceptions(true); + +$cli->addCommands([ + new DumpSchemaCommand($dependencyFactory), + new ExecuteCommand($dependencyFactory), + new GenerateCommand($dependencyFactory), + new LatestCommand($dependencyFactory), + new ListCommand($dependencyFactory), + new MigrateCommand($dependencyFactory), + new RollupCommand($dependencyFactory), + new StatusCommand($dependencyFactory), + new SyncMetadataCommand($dependencyFactory), + new VersionCommand($dependencyFactory), +]); + +try { + $cli->run(); +} catch (Exception $e) { + echo $e->getMessage(); +} diff --git a/bin/script/build b/bin/script/build new file mode 100755 index 0000000..0cf812a --- /dev/null +++ b/bin/script/build @@ -0,0 +1,3 @@ +#!/bin/bash +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +$SCRIPT_DIR/exec build \ No newline at end of file diff --git a/bin/script/down b/bin/script/down new file mode 100755 index 0000000..7b6b977 --- /dev/null +++ b/bin/script/down @@ -0,0 +1,3 @@ +#!/bin/bash +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +$SCRIPT_DIR/exec down \ No newline at end of file diff --git a/bin/script/exec b/bin/script/exec new file mode 100755 index 0000000..360cc39 --- /dev/null +++ b/bin/script/exec @@ -0,0 +1,23 @@ +#!/bin/bash + +COMMAND="$@" +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +#LOAD ENV VARIABLES FROM .ENV +export $(grep -v '^#' "${SCRIPT_DIR}/../../.env" | xargs) + +#MAC +if [[ "$OSTYPE" == "darwin"* ]]; then + docker compose -f "${SCRIPT_DIR}/../../docker/docker-compose-mac.yml" $COMMAND + +#LINUX +elif [[ "$OSTYPE" == "linux-gnu" ]]; then + docker compose -f "${SCRIPT_DIR}/../../docker/docker-compose.yml" $COMMAND + +else + echo "Dieses Skript wird auf deinem Gerät nicht unterstützt" + exit 1 +fi + +#UNSET ENV VARIABLES FROM .ENV +unset $(grep -v '^#' "${SCRIPT_DIR}/../../.env" | sed -E 's/(.*)=.*/\1/' | xargs) diff --git a/bin/script/init b/bin/script/init new file mode 100755 index 0000000..5ba2fe5 --- /dev/null +++ b/bin/script/init @@ -0,0 +1,32 @@ +#!/bin/bash + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +PROJECT_DIR=$(realpath $SCRIPT_DIR/../../) +ENV_DIR=$(realpath $PROJECT_DIR/../../) + +# Check .env file +if [ ! -f "$PROJECT_DIR/.env" ] +then + echo "Create .env file from example..." + cp "$PROJECT_DIR/.env.example" "$PROJECT_DIR/.env" + echo ".env file created, please change variables and call me again" + exit 1 +fi + +# Build and start docker containers +$SCRIPT_DIR/exec build +$SCRIPT_DIR/exec up -d + +# Source drun +source $ENV_DIR/bin/script/drun + +# Install PHP packages +drun myTube-backend composer install + +# Migrate databases to current version +drun myTube-backend composer dmm +drun myTube-backend composer dmlm + +# Insert setup for project after this line +drun myTube-backend composer console rbac:update +drun myTube-backend composer console init:data \ No newline at end of file diff --git a/bin/script/stop b/bin/script/stop new file mode 100755 index 0000000..64393e9 --- /dev/null +++ b/bin/script/stop @@ -0,0 +1,3 @@ +#!/bin/bash +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +$SCRIPT_DIR/exec stop \ No newline at end of file diff --git a/bin/script/up b/bin/script/up new file mode 100755 index 0000000..7bd3164 --- /dev/null +++ b/bin/script/up @@ -0,0 +1,3 @@ +#!/bin/bash +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +$SCRIPT_DIR/exec up -d \ No newline at end of file diff --git a/composer.development.json b/composer.development.json new file mode 100644 index 0000000..652b0e0 --- /dev/null +++ b/composer.development.json @@ -0,0 +1,130 @@ +{ + "require": { + "laminas\/laminas-servicemanager": "^3.21", + "mezzio\/mezzio-laminasrouter": "^3.8", + "reinfi\/zf-dependency-injection": "^5.4", + "laminas\/laminas-config-aggregator": "^1.13", + "doctrine\/doctrine-orm-module": "5.0.*", + "doctrine\/migrations": "^3.5", + "doctrine\/orm": "2.11.3", + "doctrine\/persistence": "2.2.*", + "ramsey\/uuid-doctrine": "^1.8", + "roave\/psr-container-doctrine": "^3.1", + "mezzio\/mezzio": "^3.17", + "mezzio\/mezzio-helpers": "^5.15", + "symfony\/cache": "5.4.8", + "doctrine\/dbal": "^3.6", + "teewurst\/psr4-advanced-wildcard-composer-plugin": "^3.0", + "laminas\/laminas-crypt": "^3.10", + "monolog\/monolog": "^3.4", + "laminas\/laminas-mail": "^2.23", + "teewurst\/pipeline": "^3.0", + "guzzlehttp\/guzzle": "^7.8", + "micilini\/video-stream": "^1.0", + "nesbot\/carbon": "^3.0" + }, + "autoload": { + "psr-4": { + "MyTube\\API\\Console\\": "src\/ApiDomain\/Console\/src", + "MyTube\\API\\External\\Authentication\\": "src\/ApiDomain\/External\/Authentication\/src", + "MyTube\\API\\External\\Health\\": "src\/ApiDomain\/External\/Health\/src", + "MyTube\\API\\External\\Tag\\": "src\/ApiDomain\/External\/Tag\/src", + "MyTube\\API\\External\\TagList\\": "src\/ApiDomain\/External\/TagList\/src", + "MyTube\\API\\External\\User\\": "src\/ApiDomain\/External\/User\/src", + "MyTube\\API\\External\\Video\\": "src\/ApiDomain\/External\/Video\/src", + "MyTube\\API\\External\\VideoList\\": "src\/ApiDomain\/External\/VideoList\/src", + "MyTube\\Data\\Business\\": "src\/DataDomain\/Business\/src", + "MyTube\\Data\\Log\\": "src\/DataDomain\/Log\/src", + "MyTube\\Handling\\Registration\\": "src\/HandlingDomain\/Registration\/src", + "MyTube\\Handling\\Role\\": "src\/HandlingDomain\/Role\/src", + "MyTube\\Handling\\Tag\\": "src\/HandlingDomain\/Tag\/src", + "MyTube\\Handling\\TagList\\": "src\/HandlingDomain\/TagList\/src", + "MyTube\\Handling\\User\\": "src\/HandlingDomain\/User\/src", + "MyTube\\Handling\\UserSession\\": "src\/HandlingDomain\/UserSession\/src", + "MyTube\\Handling\\Video\\": "src\/HandlingDomain\/Video\/src", + "MyTube\\Handling\\VideoList\\": "src\/HandlingDomain\/VideoList\/src", + "MyTube\\Infrastructure\\Database\\": "src\/Infrastructure\/Database\/src", + "MyTube\\Infrastructure\\DependencyInjection\\": "src\/Infrastructure\/DependencyInjection\/src", + "MyTube\\Infrastructure\\Encryption\\": "src\/Infrastructure\/Encryption\/src", + "MyTube\\Infrastructure\\Exception\\": "src\/Infrastructure\/Exception\/src", + "MyTube\\Infrastructure\\Logging\\": "src\/Infrastructure\/Logging\/src", + "MyTube\\Infrastructure\\Rbac\\": "src\/Infrastructure\/Rbac\/src", + "MyTube\\Infrastructure\\Request\\": "src\/Infrastructure\/Request\/src", + "MyTube\\Infrastructure\\Response\\": "src\/Infrastructure\/Response\/src", + "MyTube\\Infrastructure\\Session\\": "src\/Infrastructure\/Session\/src", + "MyTube\\Infrastructure\\UuidGenerator\\": "src\/Infrastructure\/UuidGenerator\/src" + } + }, + "extra": { + "teewurst\/psr4-advanced-wildcard-composer-plugin": { + "autoload": { + "psr-4": { + "MyTube\\API\\%s\\%s\\": "src\/ApiDomain\/{*}\/{*}\/src\/", + "MyTube\\Data\\%s\\": "src\/DataDomain\/{*}\/src\/", + "MyTube\\Handling\\%s\\": "src\/HandlingDomain\/{*}\/src\/", + "MyTube\\Infrastructure\\%s\\": "src\/Infrastructure\/{*}\/src\/" + } + }, + "autoload-dev": { + "psr-4": { + "MyTube\\API\\%s\\%s\\": "src\/ApiDomain\/{*}\/{*}\/src\/", + "MyTube\\Data\\%s\\": "src\/DataDomain\/{*}\/src\/", + "MyTube\\Handling\\%s\\": "src\/HandlingDomain\/{*}\/src\/", + "MyTube\\Infrastructure\\%s\\": "src\/Infrastructure\/{*}\/src\/" + } + } + } + }, + "scripts": { + "dmg": "php bin\/doctrine-migrations.php migrations:generate", + "dmm": "php bin\/doctrine-migrations.php migrations:migrate --no-interaction", + "dmlg": "php bin\/doctrine-migrations-log.php migrations:generate", + "dmlm": "php bin\/doctrine-migrations-log.php migrations:migrate --no-interaction", + "console": "php bin\/console.php", + "createApi": "php bin\/createApi.php", + "createPipeline": "php bin\/createPipeline.php", + "da": [ + "composer dump-autoload", + "composer dump-autoload --dev" + ] + }, + "config": { + "allow-plugins": { + "teewurst\/psr4-advanced-wildcard-composer-plugin": true + } + }, + "require-dev": { + "symfony\/dotenv": "^6.3" + }, + "autoload-dev": { + "psr-4": { + "MyTube\\API\\External\\Authentication\\": "src\/ApiDomain\/External\/Authentication\/src", + "MyTube\\API\\External\\Health\\": "src\/ApiDomain\/External\/Health\/src", + "MyTube\\API\\External\\Tag\\": "src\/ApiDomain\/External\/Tag\/src", + "MyTube\\API\\External\\TagList\\": "src\/ApiDomain\/External\/TagList\/src", + "MyTube\\API\\External\\User\\": "src\/ApiDomain\/External\/User\/src", + "MyTube\\API\\External\\Video\\": "src\/ApiDomain\/External\/Video\/src", + "MyTube\\API\\External\\VideoList\\": "src\/ApiDomain\/External\/VideoList\/src", + "MyTube\\Data\\Business\\": "src\/DataDomain\/Business\/src", + "MyTube\\Data\\Log\\": "src\/DataDomain\/Log\/src", + "MyTube\\Handling\\Registration\\": "src\/HandlingDomain\/Registration\/src", + "MyTube\\Handling\\Role\\": "src\/HandlingDomain\/Role\/src", + "MyTube\\Handling\\Tag\\": "src\/HandlingDomain\/Tag\/src", + "MyTube\\Handling\\TagList\\": "src\/HandlingDomain\/TagList\/src", + "MyTube\\Handling\\User\\": "src\/HandlingDomain\/User\/src", + "MyTube\\Handling\\UserSession\\": "src\/HandlingDomain\/UserSession\/src", + "MyTube\\Handling\\Video\\": "src\/HandlingDomain\/Video\/src", + "MyTube\\Handling\\VideoList\\": "src\/HandlingDomain\/VideoList\/src", + "MyTube\\Infrastructure\\Database\\": "src\/Infrastructure\/Database\/src", + "MyTube\\Infrastructure\\DependencyInjection\\": "src\/Infrastructure\/DependencyInjection\/src", + "MyTube\\Infrastructure\\Encryption\\": "src\/Infrastructure\/Encryption\/src", + "MyTube\\Infrastructure\\Exception\\": "src\/Infrastructure\/Exception\/src", + "MyTube\\Infrastructure\\Logging\\": "src\/Infrastructure\/Logging\/src", + "MyTube\\Infrastructure\\Rbac\\": "src\/Infrastructure\/Rbac\/src", + "MyTube\\Infrastructure\\Request\\": "src\/Infrastructure\/Request\/src", + "MyTube\\Infrastructure\\Response\\": "src\/Infrastructure\/Response\/src", + "MyTube\\Infrastructure\\Session\\": "src\/Infrastructure\/Session\/src", + "MyTube\\Infrastructure\\UuidGenerator\\": "src\/Infrastructure\/UuidGenerator\/src" + } + } +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..ffd239f --- /dev/null +++ b/composer.json @@ -0,0 +1,72 @@ +{ + "require": { + "laminas/laminas-servicemanager": "^3.21", + "mezzio/mezzio-laminasrouter": "^3.8", + "reinfi/zf-dependency-injection": "^5.4", + "laminas/laminas-config-aggregator": "^1.13", + "doctrine/doctrine-orm-module": "5.0.*", + "doctrine/migrations": "^3.5", + "doctrine/orm": "2.11.3", + "doctrine/persistence": "2.2.*", + "ramsey/uuid-doctrine": "^1.8", + "roave/psr-container-doctrine": "^3.1", + "mezzio/mezzio": "^3.17", + "mezzio/mezzio-helpers": "^5.15", + "symfony/cache": "5.4.8", + "doctrine/dbal": "^3.6", + "teewurst/psr4-advanced-wildcard-composer-plugin": "^3.0", + "laminas/laminas-crypt": "^3.10", + "monolog/monolog": "^3.4", + "laminas/laminas-mail": "^2.23", + "teewurst/pipeline": "^3.0", + "guzzlehttp/guzzle": "^7.8", + "micilini/video-stream": "^1.0", + "nesbot/carbon": "^3.0" + }, + "autoload": { + "psr-4": { + "MyTube\\API\\Console\\": "src/ApiDomain/Console/src/" + } + }, + "extra": { + "teewurst/psr4-advanced-wildcard-composer-plugin": { + "autoload": { + "psr-4": { + "MyTube\\API\\%s\\%s\\": "src/ApiDomain/{*}/{*}/src/", + "MyTube\\Data\\%s\\": "src/DataDomain/{*}/src/", + "MyTube\\Handling\\%s\\": "src/HandlingDomain/{*}/src/", + "MyTube\\Infrastructure\\%s\\": "src/Infrastructure/{*}/src/" + } + }, + "autoload-dev": { + "psr-4": { + "MyTube\\API\\%s\\%s\\": "src/ApiDomain/{*}/{*}/src/", + "MyTube\\Data\\%s\\": "src/DataDomain/{*}/src/", + "MyTube\\Handling\\%s\\": "src/HandlingDomain/{*}/src/", + "MyTube\\Infrastructure\\%s\\": "src/Infrastructure/{*}/src/" + } + } + } + }, + "scripts": { + "dmg": "php bin/doctrine-migrations.php migrations:generate", + "dmm": "php bin/doctrine-migrations.php migrations:migrate --no-interaction", + "dmlg": "php bin/doctrine-migrations-log.php migrations:generate", + "dmlm": "php bin/doctrine-migrations-log.php migrations:migrate --no-interaction", + "console": "php bin/console.php", + "createApi": "php bin/createApi.php", + "createPipeline": "php bin/createPipeline.php", + "da": [ + "composer dump-autoload", + "composer dump-autoload --dev" + ] + }, + "config": { + "allow-plugins": { + "teewurst/psr4-advanced-wildcard-composer-plugin": true + } + }, + "require-dev": { + "symfony/dotenv": "^6.3" + } +} diff --git a/config/autoload/.gitignore b/config/autoload/.gitignore new file mode 100644 index 0000000..1a83fda --- /dev/null +++ b/config/autoload/.gitignore @@ -0,0 +1,2 @@ +local.php +*.local.php diff --git a/config/autoload/api.global.php b/config/autoload/api.global.php new file mode 100644 index 0000000..bc0c3b9 --- /dev/null +++ b/config/autoload/api.global.php @@ -0,0 +1,29 @@ + [ + 'keys' => [ + 'myTube' => $_ENV['HOMEPAGE_API_KEY'], + 'notification' => $_ENV['NOTIFICATION_API_KEY'], + ], + + 'services' => [ + 'myTube' => [ + 'host' => 'myTube-backend-nginx', + 'apis' => [ + + ] + ], + 'notification' => [ + 'host' => 'notification-backend-nginx', + 'apis' => [ + 'send-mail' => [ + 'path' => '/api/mail/send', + 'method' => 'POST' + ], + ], + ], + ], + ], +]; + diff --git a/config/autoload/authorization.global.php b/config/autoload/authorization.global.php new file mode 100644 index 0000000..4c9cf75 --- /dev/null +++ b/config/autoload/authorization.global.php @@ -0,0 +1,22 @@ + [ + 'roles' => [ + 'admin', + 'user', + ], + 'permissions' => [ + 'user' => [ + 'product.product-list', + ], + 'admin' => [ + 'product.create-product', + 'product.delete-product', + 'product.product-list', + 'product.update-product', + 'user.create-user', + ], + ] + ] +]; \ No newline at end of file diff --git a/config/autoload/defines.php b/config/autoload/defines.php new file mode 100644 index 0000000..85b3a2a --- /dev/null +++ b/config/autoload/defines.php @@ -0,0 +1,6 @@ + [ + // Use 'aliases' to alias a service name to another service. The + // key is the alias name, the value is the service to which it points. + 'aliases' => [ + // Fully\Qualified\ClassOrInterfaceName::class => Fully\Qualified\ClassName::class, + ], + // Use 'invokables' for constructor-less services, or services that do + // not require arguments to the constructor. Map a service name to the + // class name. + 'invokables' => [ + // Fully\Qualified\InterfaceName::class => Fully\Qualified\ClassName::class, + ], + // Use 'factories' for services provided by callbacks/factory classes. + 'factories' => [ + // Fully\Qualified\ClassName::class => Fully\Qualified\FactoryName::class, + ], + ], +]; diff --git a/config/autoload/doctrine.global.php b/config/autoload/doctrine.global.php new file mode 100644 index 0000000..c32e202 --- /dev/null +++ b/config/autoload/doctrine.global.php @@ -0,0 +1,14 @@ + [ + 'types' => [ + UuidBinaryOrderedTimeType::NAME => UuidBinaryOrderedTimeType::class, + ], + ], +]; diff --git a/config/autoload/logger.global.php b/config/autoload/logger.global.php new file mode 100644 index 0000000..9c5cf69 --- /dev/null +++ b/config/autoload/logger.global.php @@ -0,0 +1,14 @@ + [ + 'name' => 'myTube.backend', + 'path' => APP_ROOT . '/var/log/myTube.backend.log', + 'level' => Level::Debug, + 'pretty' => true, + ] +]; diff --git a/config/autoload/mezzio.global.php b/config/autoload/mezzio.global.php new file mode 100644 index 0000000..20e3526 --- /dev/null +++ b/config/autoload/mezzio.global.php @@ -0,0 +1,24 @@ + false, + + // Enable debugging; typically used to provide debugging information within myTubes. + 'debug' => false, + 'mezzio' => [ + // Provide myTubes for the error handling middleware to use when + // generating responses. + 'error_handler' => [ + 'myTube_404' => 'error::404', + 'myTube_error' => 'error::error', + ], + ], +]; diff --git a/config/config.php b/config/config.php new file mode 100644 index 0000000..b8264f6 --- /dev/null +++ b/config/config.php @@ -0,0 +1,97 @@ + 'data/cache/config-cache.php', +]; + +$aggregator = new ConfigAggregator([ + // Include cache configuration + new ArrayProvider($cacheConfig), + \Mezzio\Helper\ConfigProvider::class, + \Mezzio\ConfigProvider::class, + \Mezzio\Router\ConfigProvider::class, + \Mezzio\Router\LaminasRouter\ConfigProvider::class, + + \Laminas\Diactoros\ConfigProvider::class, + \Laminas\HttpHandlerRunner\ConfigProvider::class, + \Laminas\Validator\ConfigProvider::class, + \Laminas\Router\ConfigProvider::class, + + \Reinfi\DependencyInjection\ConfigProvider::class, + + \DoctrineORMModule\ConfigProvider::class, + \DoctrineModule\ConfigProvider::class, + + + // Swoole config to overwrite some services (if installed) + class_exists(\Mezzio\Swoole\ConfigProvider::class) + ? \Mezzio\Swoole\ConfigProvider::class + : function (): array { + return []; + }, + + + // Data + \MyTube\Data\Business\ConfigProvider::class, + \MyTube\Data\Log\ConfigProvider::class, + + // Infrastructure + \MyTube\Infrastructure\Database\ConfigProvider::class, + \MyTube\Infrastructure\DependencyInjection\ConfigProvider::class, + \MyTube\Infrastructure\Encryption\ConfigProvider::class, + \MyTube\Infrastructure\Exception\ConfigProvider::class, + \MyTube\Infrastructure\Logging\ConfigProvider::class, + \MyTube\Infrastructure\Rbac\ConfigProvider::class, + \MyTube\Infrastructure\Request\ConfigProvider::class, + \MyTube\Infrastructure\Session\ConfigProvider::class, + + // HandlingDomain + \MyTube\Handling\User\ConfigProvider::class, + \MyTube\Handling\UserSession\ConfigProvider::class, + \MyTube\Handling\Registration\ConfigProvider::class, + \MyTube\Handling\Video\ConfigProvider::class, + \MyTube\Handling\VideoList\ConfigProvider::class, + \MyTube\Handling\Tag\ConfigProvider::class, + \MyTube\Handling\TagList\ConfigProvider::class, + + // API + /// Command + \MyTube\API\Console\ConfigProvider::class, + + /// External + \MyTube\API\External\Health\ConfigProvider::class, + \MyTube\API\External\User\ConfigProvider::class, + \MyTube\API\External\Authentication\ConfigProvider::class, + \MyTube\API\External\Video\ConfigProvider::class, + \MyTube\API\External\VideoList\ConfigProvider::class, + \MyTube\API\External\Tag\ConfigProvider::class, + \MyTube\API\External\TagList\ConfigProvider::class, + + /// Internal + + + + + + + // Load application config in a pre-defined order in such a way that local settings + // overwrite global settings. (Loaded as first to last): + // - `global.php` + // - `*.global.php` + // - `local.php` + // - `*.local.php` + new PhpFileProvider(realpath(__DIR__) . '/autoload/{{,*.}global,{,*.}local}.php'), + + // Load development config if it exists + new PhpFileProvider(realpath(__DIR__) . '/development.config.php'), +], $cacheConfig['config_cache_path']); + +return $aggregator->getMergedConfig(); diff --git a/config/container.php b/config/container.php new file mode 100644 index 0000000..e13f484 --- /dev/null +++ b/config/container.php @@ -0,0 +1,21 @@ +load(__DIR__ . '/../.env'); +} + +// Load configuration +$config = require __DIR__ . '/config.php'; + +$dependencies = $config['dependencies']; +$dependencies['services']['config'] = $config; + +// Build container +return new ServiceManager($dependencies); diff --git a/config/development.config.php b/config/development.config.php new file mode 100644 index 0000000..94b06d2 --- /dev/null +++ b/config/development.config.php @@ -0,0 +1,31 @@ + true, + ConfigAggregator::ENABLE_CACHE => false, +]; diff --git a/config/pipeline.php b/config/pipeline.php new file mode 100644 index 0000000..290af6e --- /dev/null +++ b/config/pipeline.php @@ -0,0 +1,94 @@ +pipe(ErrorHandler::class); + $app->pipe(ServerUrlMiddleware::class); + + // Pipe more middleware here that you want to execute on every request: + // - bootstrapping + // - pre-conditions + // - modifications to outgoing responses + // + // Piped Middleware may be either callables or service names. Middleware may + // also be passed as an array; each item in the array must resolve to + // middleware eventually (i.e., callable or service name). + // + // Middleware can be attached to specific paths, allowing you to mix and match + // applications under a common domain. The handlers in each middleware + // attached this way will see a URI with the matched path segment removed. + // + // i.e., path of "/api/member/profile" only passes "/member/profile" to $apiMiddleware + // - $app->pipe('/api', $apiMiddleware); + // - $app->pipe('/docs', $apiDocMiddleware); + // - $app->pipe('/files', $filesMiddleware); + + // Register the routing middleware in the middleware pipeline. + // This middleware registers the Mezzio\Router\RouteResult request attribute. + $app->pipe(RouteMiddleware::class); + + // The following handle routing failures for common conditions: + // - HEAD request but no routes answer that method + // - OPTIONS request but no routes answer that method + // - method not allowed + // Order here matters; the MethodNotAllowedMiddleware should be placed + // after the Implicit*Middleware. + $app->pipe(ImplicitHeadMiddleware::class); + $app->pipe(ImplicitOptionsMiddleware::class); + $app->pipe(MethodNotAllowedMiddleware::class); + + // Seed the UrlHelper with the routing results: + $app->pipe(UrlHelperMiddleware::class); + + + + + //// Pre MyTube Space + $app->pipe(MyTubeExceptionHandlerMiddleware::class); + $app->pipe(AnalyzeHeaderMiddleware::class); + $app->pipe(AnalyzeBodyMiddleware::class); + + //// MyTube Space + $app->pipe(SessionMiddleware::class); + + + + // Add more middleware here that needs to introspect the routing results; this + // might include: + // + // - route-based authentication + // - route-based validation + // - etc. + + // Register the dispatch middleware in the middleware pipeline + $app->pipe(DispatchMiddleware::class); + + // At this point, if no Response is returned by any middleware, the + // NotFoundHandler kicks in; alternately, you can provide other fallback + // middleware to execute. + $app->pipe(NotFoundHandler::class); +}; diff --git a/config/routes.php b/config/routes.php new file mode 100644 index 0000000..054a32d --- /dev/null +++ b/config/routes.php @@ -0,0 +1,52 @@ +get('/', App\Handler\HomePageHandler::class, 'home'); + * $app->post('/album', App\Handler\AlbumCreateHandler::class, 'album.create'); + * $app->put('/album/:id', App\Handler\AlbumUpdateHandler::class, 'album.put'); + * $app->patch('/album/:id', App\Handler\AlbumUpdateHandler::class, 'album.patch'); + * $app->delete('/album/:id', App\Handler\AlbumDeleteHandler::class, 'album.delete'); + * + * Or with multiple request methods: + * + * $app->route('/contact', App\Handler\ContactHandler::class, ['GET', 'POST', ...], 'contact'); + * + * Or handling all request methods: + * + * $app->route('/contact', App\Handler\ContactHandler::class)->setName('contact'); + * + * or: + * + * $app->route( + * '/contact', + * App\Handler\ContactHandler::class, + * Mezzio\Router\Route::HTTP_METHOD_ANY, + * 'contact' + * ); + */ + +return static function (Application $app, MiddlewareFactory $factory, ContainerInterface $container): void { + $config = $container->get('config'); + + foreach ($config['routes'] as $routeConfig) { + $app->route( + name: $routeConfig['name'], + path: $routeConfig['path'], + middleware: $routeConfig['middleware'], + methods: $routeConfig['allowed_methods'] + ); + } +}; diff --git a/data/migrations/log/Version20230922150649.php b/data/migrations/log/Version20230922150649.php new file mode 100644 index 0000000..993e73c --- /dev/null +++ b/data/migrations/log/Version20230922150649.php @@ -0,0 +1,40 @@ +addSql($query); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP TABLE log'); + } +} diff --git a/data/migrations/myTube/Version20230922085011.php b/data/migrations/myTube/Version20230922085011.php new file mode 100644 index 0000000..6c124bc --- /dev/null +++ b/data/migrations/myTube/Version20230922085011.php @@ -0,0 +1,32 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $this->addSql("DROP TABLE role;"); + } +} diff --git a/data/migrations/myTube/Version20230922092351.php b/data/migrations/myTube/Version20230922092351.php new file mode 100644 index 0000000..c8221d5 --- /dev/null +++ b/data/migrations/myTube/Version20230922092351.php @@ -0,0 +1,35 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $this->addSql("DROP TABLE permission;"); + } +} diff --git a/data/migrations/myTube/Version20230922092754.php b/data/migrations/myTube/Version20230922092754.php new file mode 100644 index 0000000..0b4b9e2 --- /dev/null +++ b/data/migrations/myTube/Version20230922092754.php @@ -0,0 +1,36 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $this->addSql("DROP TABLE role_permission;"); + } +} diff --git a/data/migrations/myTube/Version20230922101354.php b/data/migrations/myTube/Version20230922101354.php new file mode 100644 index 0000000..6c19b61 --- /dev/null +++ b/data/migrations/myTube/Version20230922101354.php @@ -0,0 +1,41 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $this->addSql("DROP TABLE user;"); + } +} diff --git a/data/migrations/myTube/Version20230922101355.php b/data/migrations/myTube/Version20230922101355.php new file mode 100644 index 0000000..f4b2e3f --- /dev/null +++ b/data/migrations/myTube/Version20230922101355.php @@ -0,0 +1,38 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $this->addSql("DROP TABLE role_permission;"); + } +} diff --git a/data/migrations/myTube/Version20230924113403.php b/data/migrations/myTube/Version20230924113403.php new file mode 100644 index 0000000..c7245c5 --- /dev/null +++ b/data/migrations/myTube/Version20230924113403.php @@ -0,0 +1,37 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $this->addSql("DROP TABLE registration;"); + } +} diff --git a/data/migrations/myTube/Version20240214194156.php b/data/migrations/myTube/Version20240214194156.php new file mode 100644 index 0000000..ec19ffc --- /dev/null +++ b/data/migrations/myTube/Version20240214194156.php @@ -0,0 +1,38 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $this->addSql("DROP TABLE video;"); + } +} diff --git a/data/migrations/myTube/Version20240223130626.php b/data/migrations/myTube/Version20240223130626.php new file mode 100644 index 0000000..4352b9b --- /dev/null +++ b/data/migrations/myTube/Version20240223130626.php @@ -0,0 +1,37 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $this->addSql("DROP TABLE tag;"); + } +} diff --git a/data/migrations/myTube/Version20240223130835.php b/data/migrations/myTube/Version20240223130835.php new file mode 100644 index 0000000..917cb80 --- /dev/null +++ b/data/migrations/myTube/Version20240223130835.php @@ -0,0 +1,38 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $this->addSql("DROP TABLE tag_alias;"); + } +} diff --git a/data/migrations/myTube/Version20240223142111.php b/data/migrations/myTube/Version20240223142111.php new file mode 100644 index 0000000..06b8963 --- /dev/null +++ b/data/migrations/myTube/Version20240223142111.php @@ -0,0 +1,35 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $this->addSql("DROP TABLE video_tag;"); + } +} diff --git a/data/migrations/myTube/Version20240229205003.php b/data/migrations/myTube/Version20240229205003.php new file mode 100644 index 0000000..da3bc9e --- /dev/null +++ b/data/migrations/myTube/Version20240229205003.php @@ -0,0 +1,37 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $this->addSql("DROP TABLE video_event;"); + } +} diff --git a/data/migrations/myTube/Version20240323095155.php b/data/migrations/myTube/Version20240323095155.php new file mode 100644 index 0000000..c54a1df --- /dev/null +++ b/data/migrations/myTube/Version20240323095155.php @@ -0,0 +1,35 @@ +addSql($sql); + } + + public function down(Schema $schema): void + { + $this->addSql("ALTER TABLE video + DROP COLUMN duration + ;"); + } +} diff --git a/docker/docker-compose-mac.yml b/docker/docker-compose-mac.yml new file mode 100644 index 0000000..d2dc212 --- /dev/null +++ b/docker/docker-compose-mac.yml @@ -0,0 +1,51 @@ +version: '3' +networks: + myTube: + external: true + +services: + myTube-backend-mysql: + image: myTube-backend-mysql + networks: + - myTube + build: + context: ./../ + dockerfile: ./docker/mysql/dockerfile + volumes: + - /Users/flo/dev/backend/myTube/var/db:/var/lib/mysql:z + environment: + MYSQL_USER: ${DB_USER} + MYSQL_PASSWORD: ${DB_PASSWORD} + ports: + - 3306:3306 + healthcheck: + test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"] + timeout: 20s + retries: 10 + + myTube-backend-app: + image: myTube-backend-app + networks: + - myTube + build: + context: ./../ + dockerfile: ./docker/php/dockerfile + volumes: + - /Users/flo/dev/backend/myTube/:/var/www/html:z + ports: + - 9000:9000 + depends_on: + myTube-backend-mysql: + condition: service_healthy + + myTube-backend-nginx: + image: myTube-backend-nginx + networks: + - myTube + build: + context: ./../ + dockerfile: ./docker/nginx/dockerfile + ports: + - 8080:80 + depends_on: + - myTube-backend-app \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..f5a39a0 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,51 @@ +version: '3' +networks: + mytube: + external: true + +services: + mytube-backend-mysql: + image: mytube-backend-mysql + networks: + - mytube + build: + context: ./../ + dockerfile: ./docker/mysql/dockerfile + volumes: + - ./../var/db:/var/lib/mysql:z + environment: + MYSQL_USER: ${DB_USER} + MYSQL_PASSWORD: ${DB_PASSWORD} + ports: + - 3306:3306 + healthcheck: + test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"] + timeout: 20s + retries: 10 + + mytube-backend-app: + image: mytube-backend-app + networks: + - mytube + build: + context: ./../ + dockerfile: ./docker/php/dockerfile + volumes: + - ./../:/var/www/html:z + ports: + - 9000:9000 + depends_on: + mytube-backend-mysql: + condition: service_healthy + + mytube-backend-nginx: + image: mytube-backend-nginx + networks: + - mytube + build: + context: ./../ + dockerfile: ./docker/nginx/dockerfile + ports: + - 8080:80 + depends_on: + - mytube-backend-app \ No newline at end of file diff --git a/docker/mysql/dockerfile b/docker/mysql/dockerfile new file mode 100644 index 0000000..8261387 --- /dev/null +++ b/docker/mysql/dockerfile @@ -0,0 +1,7 @@ +FROM mysql:latest + +ENV MYSQL_RANDOM_ROOT_PASSWORD=1 + +COPY docker/mysql/scripts /docker-entrypoint-initdb.d/ + +CMD ["mysqld"] \ No newline at end of file diff --git a/docker/mysql/scripts/initdb.sql b/docker/mysql/scripts/initdb.sql new file mode 100644 index 0000000..ed527bc --- /dev/null +++ b/docker/mysql/scripts/initdb.sql @@ -0,0 +1,4 @@ +CREATE DATABASE IF NOT EXISTS `log`; +CREATE DATABASE IF NOT EXISTS `myTube`; + +GRANT ALL PRIVILEGES on *.* to 'myTube'@'%'; \ No newline at end of file diff --git a/docker/nginx/config/nginx.conf b/docker/nginx/config/nginx.conf new file mode 100644 index 0000000..cd11c50 --- /dev/null +++ b/docker/nginx/config/nginx.conf @@ -0,0 +1,17 @@ +upstream host-backend-app { + server myTube-backend-app:9000; +} + +server { + listen 80 default_server; + + client_max_body_size 10000M; + + location / { + fastcgi_pass host-backend-app; + fastcgi_index index.php; + + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /var/www/html/public/index.php; + } +} \ No newline at end of file diff --git a/docker/nginx/config/upstream.conf b/docker/nginx/config/upstream.conf new file mode 100644 index 0000000..e12c63a --- /dev/null +++ b/docker/nginx/config/upstream.conf @@ -0,0 +1,3 @@ +upstream backend-dev { + server backend-app:9000; +} \ No newline at end of file diff --git a/docker/nginx/dockerfile b/docker/nginx/dockerfile new file mode 100644 index 0000000..f2aba1c --- /dev/null +++ b/docker/nginx/dockerfile @@ -0,0 +1,5 @@ +FROM nginx:alpine + +COPY docker/nginx/config/nginx.conf /etc/nginx/conf.d/default.conf + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/php/config/uploads.ini b/docker/php/config/uploads.ini new file mode 100644 index 0000000..db9e741 --- /dev/null +++ b/docker/php/config/uploads.ini @@ -0,0 +1,6 @@ +file_uploads = On +max_file_uploads = 1000 +memory_limit = 10000M +upload_max_filesize = 10000M +post_max_size = 10000M +max_execution_time = 1000 \ No newline at end of file diff --git a/docker/php/dockerfile b/docker/php/dockerfile new file mode 100644 index 0000000..2b73631 --- /dev/null +++ b/docker/php/dockerfile @@ -0,0 +1,18 @@ +FROM php:8.1-fpm + +RUN apt-get -y update +RUN apt-get -y install git +RUN apt-get install -y ffmpeg + +RUN chown -R www-data:www-data /var/www/html +WORKDIR /var/www/html + +RUN docker-php-ext-install pdo pdo_mysql +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer + +RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo +USER docker + +COPY docker/php/config/uploads.ini /usr/local/etc/php/conf.d + +CMD ["php-fpm"] \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..f0e7212 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,26 @@ + + + + + test + + + + + + src + + + diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..b43deeb --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,19 @@ +RewriteEngine On +# The following rule allows authentication to work with fast-cgi +RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +# The following rule tells Apache that if the requested filename +# exists, simply serve it. +RewriteCond %{REQUEST_FILENAME} -f [OR] +RewriteCond %{REQUEST_FILENAME} -l [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteRule ^ - [NC,L] + +# The following rewrites all other queries to index.php. The +# condition ensures that if you are using Apache aliases to do +# mass virtual hosting, the base path will be prepended to +# allow proper resolution of the index.php file; it will work +# in non-aliased environments as well, providing a safe, one-size +# fits all solution. +RewriteCond $0::%{REQUEST_URI} ^([^:]*+(?::[^:]*+)*?)::(/.+?)\1$ +RewriteRule .+ - [E=BASE:%2] +RewriteRule .* %{ENV:BASE}index.php [NC,L] diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..0323e39 --- /dev/null +++ b/public/index.php @@ -0,0 +1,48 @@ +getMessage(); + $e = $e->getPrevious(); + } + return $messageArray; +} + +function throwableToMessageArrayString(Throwable $e): string { + return implode( PHP_EOL, throwableToMessageArray($e) ); +} + +/** + * Self-called anonymous function that creates its own scope and keeps the global namespace clean. + */ +(function () { + + /** @var \Psr\Container\ContainerInterface $container */ + $container = require './../config/container.php'; + + /** @var Application $app */ + $app = $container->get(Application::class); + $factory = $container->get(MiddlewareFactory::class); + + // Execute programmatic/declarative middleware pipeline and routing + // configuration statements + (require './../config/pipeline.php')($app, $factory, $container); + (require './../config/routes.php')($app, $factory, $container); + + $app->run(); + +})(); diff --git a/src/ApiDomain/Console/config/console.php b/src/ApiDomain/Console/config/console.php new file mode 100644 index 0000000..1c9a59c --- /dev/null +++ b/src/ApiDomain/Console/config/console.php @@ -0,0 +1,15 @@ + [ + InitializeDataCommand::class, + RbacUpdateCommand::class, + AnalyzeVideoTitlesCommand::class, + ReadUntaggedVideosCommand::class, + ] +]; diff --git a/src/ApiDomain/Console/config/service_manager.php b/src/ApiDomain/Console/config/service_manager.php new file mode 100644 index 0000000..6df4f49 --- /dev/null +++ b/src/ApiDomain/Console/config/service_manager.php @@ -0,0 +1,16 @@ + [ + InitializeDataCommand::class => AutoWiringFactory::class, + RbacUpdateCommand::class => AutoWiringFactory::class, + AnalyzeVideoTitlesCommand::class => AutoWiringFactory::class, + ReadUntaggedVideosCommand::class => AutoWiringFactory::class, + ], +]; diff --git a/src/ApiDomain/Console/src/Command/AnalyzeVideoTitlesCommand.php b/src/ApiDomain/Console/src/Command/AnalyzeVideoTitlesCommand.php new file mode 100644 index 0000000..7d37057 --- /dev/null +++ b/src/ApiDomain/Console/src/Command/AnalyzeVideoTitlesCommand.php @@ -0,0 +1,64 @@ +getName()); + + $this->videoRepository = $this->entityManager->getRepository(Video::class); + } + + protected function execute( + InputInterface $input, + OutputInterface $output + ): int { + $io = new SymfonyStyle($input, $output); + + try { + $videos = $this->videoRepository->findAll(); + + /** @var Video $video */ + foreach ($videos as $video) { + $io->info($video->getTitle()); + + $this->titleAnalyzer->analyze($video); + $this->durationAnalyzer->analyze($video); + + $this->entityManager->persist($video); + $this->entityManager->flush(); + } + + $io->success('OK!'); + } catch (\Throwable $e) { + $io->error($e->getMessage()); + $io->error($e->getTraceAsString()); + $this->logger->error($e->getMessage(), ['exception' => $e]); + return Command::FAILURE; + } + + return Command::SUCCESS; + } +} diff --git a/src/ApiDomain/Console/src/Command/InitializeDataCommand.php b/src/ApiDomain/Console/src/Command/InitializeDataCommand.php new file mode 100644 index 0000000..52158d6 --- /dev/null +++ b/src/ApiDomain/Console/src/Command/InitializeDataCommand.php @@ -0,0 +1,80 @@ +getName()); + + $this->roleRepository = $this->entityManager->getRepository(Role::class); + $this->userRepository = $this->entityManager->getRepository(User::class); + } + + protected function execute( + InputInterface $input, + OutputInterface $output + ): int { + $io = new SymfonyStyle($input, $output); + + try { + $admin = $this->roleRepository->findOneBy([ + 'identifier' => 'admin' + ]) ?? null; + if ($admin === null) { + throw new \Exception('Admin role does not exist!'); + } + + $adminUser = $this->userRepository->findOneBy([ + 'roleId' => $admin->getId() + ]) ?? null; + + if ($adminUser === null) { + $io->info('Create admin'); + + $adminUser = new User(); + $adminUser->setRole($admin); + $adminUser->setUsername($_ENV['INIT_USER_NAME']); + $adminUser->setMail($_ENV['INIT_USER_MAIL']); + $adminUser->setPassword( + $this->encryptionClient->encrypt( + $_ENV['INIT_USER_PASSWORD'] + ) + ); + + $this->entityManager->persist($adminUser); + $this->entityManager->flush(); + } + + $io->success('OK!'); + } catch (\Throwable $e) { + $io->error($e->getMessage()); + $io->error($e->getTraceAsString()); + $this->logger->error($e->getMessage(), ['exception' => $e]); + return Command::FAILURE; + } + + return Command::SUCCESS; + } +} diff --git a/src/ApiDomain/Console/src/Command/RbacUpdateCommand.php b/src/ApiDomain/Console/src/Command/RbacUpdateCommand.php new file mode 100644 index 0000000..924494d --- /dev/null +++ b/src/ApiDomain/Console/src/Command/RbacUpdateCommand.php @@ -0,0 +1,98 @@ +getName()); + + $this->roleRepository = $this->entityManager->getRepository(Role::class); + $this->permissionRepository = $this->entityManager->getRepository(Permission::class); + + $this->rbacConfig = $this->configService->resolve('myTube-rbac')->toArray(); + } + + protected function execute( + InputInterface $input, + OutputInterface $output + ): int { + $io = new SymfonyStyle($input, $output); + + try { + + $roles = []; + + foreach ($this->rbacConfig['roles'] as $roleIdentifier) { + $role = $this->roleRepository->findOneBy([ + 'identifier' => $roleIdentifier + ]) ?? null; + + if ($role === null) { + $role = new Role(); + $role->setIdentifier($roleIdentifier); + + $this->entityManager->persist($role); + $this->entityManager->flush(); + } + + $roles[$roleIdentifier] = $role; + } + + foreach ($roles as $role) { + $roleIdentifier = $role->getIdentifier(); + + foreach ($this->rbacConfig['permissions'][$roleIdentifier] as $permissionIdentifier) { + $io->info($role->getIdentifier() . '.' . $permissionIdentifier); + + $permission = $this->permissionRepository->findOneBy([ + 'identifier' => $permissionIdentifier + ]) ?? null; + + if ($permission === null) { + $permission = new Permission(); + $permission->setIdentifier($permissionIdentifier); + + $this->entityManager->persist($permission); + $this->entityManager->flush(); + } + + $role->addPermission($permission); + $this->entityManager->persist($role); + $this->entityManager->flush(); + } + } + + $io->success('OK!'); + } catch (\Throwable $e) { + $io->error($e->getMessage()); + $io->error($e->getTraceAsString()); + $this->logger->error($e->getMessage(), ['exception' => $e]); + return Command::FAILURE; + } + + return Command::SUCCESS; + } +} diff --git a/src/ApiDomain/Console/src/Command/ReadUntaggedVideosCommand.php b/src/ApiDomain/Console/src/Command/ReadUntaggedVideosCommand.php new file mode 100644 index 0000000..68fb543 --- /dev/null +++ b/src/ApiDomain/Console/src/Command/ReadUntaggedVideosCommand.php @@ -0,0 +1,64 @@ +getName()); + + $this->videoRepository = $this->entityManager->getRepository(Video::class); + } + + protected function execute( + InputInterface $input, + OutputInterface $output + ): int { + $io = new SymfonyStyle($input, $output); + + try { + $qb = $this->videoRepository + ->createQueryBuilder('v') + ->leftJoin('v.tags', 't') + ->groupBy('v.id') + ->having('COUNT(t.id) = 0') + ->orderBy('v.title'); + + $videos = $qb->getQuery()->execute(); + + /** @var Video $video */ + foreach ($videos as $video) { + echo $video->getTitle() . PHP_EOL; + } + + $io->info('total: ' . count($videos)); + + $io->success('OK!'); + } catch (\Throwable $e) { + $io->error($e->getMessage()); + $io->error($e->getTraceAsString()); + $this->logger->error($e->getMessage(), ['exception' => $e]); + return Command::FAILURE; + } + + return Command::SUCCESS; + } +} diff --git a/src/ApiDomain/Console/src/ConfigProvider.php b/src/ApiDomain/Console/src/ConfigProvider.php new file mode 100644 index 0000000..f09ed3b --- /dev/null +++ b/src/ApiDomain/Console/src/ConfigProvider.php @@ -0,0 +1,16 @@ + require __DIR__ . './../config/service_manager.php', + 'console' => require __DIR__ . '/./../config/console.php', + ]; + } +} diff --git a/src/ApiDomain/External/Authentication/config/routes.php b/src/ApiDomain/External/Authentication/config/routes.php new file mode 100644 index 0000000..bf8cc8d --- /dev/null +++ b/src/ApiDomain/External/Authentication/config/routes.php @@ -0,0 +1,43 @@ + 'auth.login-user', + 'path' => '/api/auth/login-user', + 'allowed_methods' => ['POST'], + 'middleware' => [ + LoginUserHandler::class + ], + ], + [ + 'name' => 'auth.logout-user', + 'path' => '/api/auth/logout-user', + 'allowed_methods' => ['POST'], + 'middleware' => [ + LoggedInUserMiddleware::class, + LogoutUserHandler::class + ], + ], + [ + 'name' => 'auth.confirm-registration', + 'path' => '/api/auth/confirm-registration', + 'allowed_methods' => ['POST'], + 'middleware' => [ + ConfirmRegistrationHandler::class + ], + ], + [ + 'name' => 'auth.register-user', + 'path' => '/api/auth/register-user', + 'allowed_methods' => ['POST'], + 'middleware' => [ + RegisterUserHandler::class + ], + ], +]; \ No newline at end of file diff --git a/src/ApiDomain/External/Authentication/config/service_manager.php b/src/ApiDomain/External/Authentication/config/service_manager.php new file mode 100644 index 0000000..8594deb --- /dev/null +++ b/src/ApiDomain/External/Authentication/config/service_manager.php @@ -0,0 +1,17 @@ + [ + // Handler + LoginUserHandler::class => AutoWiringFactory::class, + LogoutUserHandler::class => AutoWiringFactory::class, + ConfirmRegistrationHandler::class => AutoWiringFactory::class, + RegisterUserHandler::class => AutoWiringFactory::class + ], +]; diff --git a/src/ApiDomain/External/Authentication/src/ConfigProvider.php b/src/ApiDomain/External/Authentication/src/ConfigProvider.php new file mode 100644 index 0000000..3080278 --- /dev/null +++ b/src/ApiDomain/External/Authentication/src/ConfigProvider.php @@ -0,0 +1,16 @@ + require __DIR__ . '/./../config/service_manager.php', + 'routes' => require __DIR__ . '/./../config/routes.php', + ]; + } +} diff --git a/src/ApiDomain/External/Authentication/src/Handler/ConfirmRegistrationHandler.php b/src/ApiDomain/External/Authentication/src/Handler/ConfirmRegistrationHandler.php new file mode 100644 index 0000000..46720b1 --- /dev/null +++ b/src/ApiDomain/External/Authentication/src/Handler/ConfirmRegistrationHandler.php @@ -0,0 +1,42 @@ +getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $query = $this->builder->build( + Uuid::fromString($data['id']), + $data['password'], + $data['passwordConfirmation'], + ); + + $result = $this->handler->execute($query); + return new JsonResponse( + $this->userFormatter->format($result) + ); + } +} diff --git a/src/ApiDomain/External/Authentication/src/Handler/LoginUserHandler.php b/src/ApiDomain/External/Authentication/src/Handler/LoginUserHandler.php new file mode 100644 index 0000000..04605f5 --- /dev/null +++ b/src/ApiDomain/External/Authentication/src/Handler/LoginUserHandler.php @@ -0,0 +1,40 @@ +getAttribute(SessionMiddleware::SESSION_ATTRIBUTE); + $data = $request->getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $query = $this->builder->build( + $session, + $data['identifier'], + $data['password'], + ); + + $result = $this->handler->execute($query); + return new JsonResponse([ + 'sessionId' => $result->getId()->toString() + ]); + } +} diff --git a/src/ApiDomain/External/Authentication/src/Handler/LogoutUserHandler.php b/src/ApiDomain/External/Authentication/src/Handler/LogoutUserHandler.php new file mode 100644 index 0000000..f68dd29 --- /dev/null +++ b/src/ApiDomain/External/Authentication/src/Handler/LogoutUserHandler.php @@ -0,0 +1,35 @@ +getAttribute(SessionMiddleware::SESSION_ATTRIBUTE); + + $query = $this->builder->build( + $session + ); + $this->handler->execute($query); + + return new JsonResponse('OK'); + } +} diff --git a/src/ApiDomain/External/Authentication/src/Handler/RegisterUserHandler.php b/src/ApiDomain/External/Authentication/src/Handler/RegisterUserHandler.php new file mode 100644 index 0000000..4f5a3e2 --- /dev/null +++ b/src/ApiDomain/External/Authentication/src/Handler/RegisterUserHandler.php @@ -0,0 +1,43 @@ +getAttribute(AnalyzeHeaderMiddleware::HOST_ATTRIBUTE); + $data = $request->getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $query = $this->builder->build( + $data['username'], + $data['mail'], + $host + ); + + $this->handler->execute($query); + return new SuccessResponse(); + } +} diff --git a/src/ApiDomain/External/Health/config/routes.php b/src/ApiDomain/External/Health/config/routes.php new file mode 100644 index 0000000..7b3cc22 --- /dev/null +++ b/src/ApiDomain/External/Health/config/routes.php @@ -0,0 +1,16 @@ + 'health', + 'path' => '/api/health', + 'allowed_methods' => ['GET'], + 'middleware' => [ + HealthHandler::class + ], + ], +]; + +?> \ No newline at end of file diff --git a/src/ApiDomain/External/Health/config/service_manager.php b/src/ApiDomain/External/Health/config/service_manager.php new file mode 100644 index 0000000..6a17420 --- /dev/null +++ b/src/ApiDomain/External/Health/config/service_manager.php @@ -0,0 +1,12 @@ + [ + HealthHandler::class => AutoWiringFactory::class + ], +] + +?> \ No newline at end of file diff --git a/src/ApiDomain/External/Health/src/ConfigProvider.php b/src/ApiDomain/External/Health/src/ConfigProvider.php new file mode 100644 index 0000000..d4d3beb --- /dev/null +++ b/src/ApiDomain/External/Health/src/ConfigProvider.php @@ -0,0 +1,16 @@ + require __DIR__ . './../config/service_manager.php', + 'routes' => require __DIR__ . '/./../config/routes.php', + ]; + } +} diff --git a/src/ApiDomain/External/Health/src/Handler/HealthHandler.php b/src/ApiDomain/External/Health/src/Handler/HealthHandler.php new file mode 100644 index 0000000..5abf241 --- /dev/null +++ b/src/ApiDomain/External/Health/src/Handler/HealthHandler.php @@ -0,0 +1,23 @@ + diff --git a/src/ApiDomain/External/Tag/config/routes.php b/src/ApiDomain/External/Tag/config/routes.php new file mode 100644 index 0000000..a40efa9 --- /dev/null +++ b/src/ApiDomain/External/Tag/config/routes.php @@ -0,0 +1,41 @@ + 'tag.create', + 'path' => '/api/tag/create[/]', + 'allowed_methods' => ['POST'], + 'middleware' => [ + CreateHandler::class, + ], + ], + [ + 'name' => 'tag.add-alias', + 'path' => '/api/tag/add-alias[/]', + 'allowed_methods' => ['POST'], + 'middleware' => [ + AddAliasHandler::class, + ], + ], + [ + 'name' => 'tag.read-details', + 'path' => '/api/tag/read-details[/]', + 'allowed_methods' => ['POST'], + 'middleware' => [ + ReadDetailsHandler::class, + ], + ], + [ + 'name' => 'tag.read-video-list', + 'path' => '/api/tag/read-video-list[/]', + 'allowed_methods' => ['POST'], + 'middleware' => [ + ReadVideoListHandler::class, + ], + ], +]; diff --git a/src/ApiDomain/External/Tag/config/service_manager.php b/src/ApiDomain/External/Tag/config/service_manager.php new file mode 100644 index 0000000..a3ba791 --- /dev/null +++ b/src/ApiDomain/External/Tag/config/service_manager.php @@ -0,0 +1,27 @@ + [ + // Handler + AddAliasHandler::class => AutoWiringFactory::class, + CreateHandler::class => AutoWiringFactory::class, + ReadDetailsHandler::class => AutoWiringFactory::class, + ReadVideoListHandler::class => AutoWiringFactory::class, + + // Response Formatter + AddAliasResponseFormatter::class => AutoWiringFactory::class, + CreateResponseFormatter::class => AutoWiringFactory::class, + ReadDetailsResponseFormatter::class => AutoWiringFactory::class, + ReadVideoListResponseFormatter::class => AutoWiringFactory::class, + ], +]; diff --git a/src/ApiDomain/External/Tag/src/ConfigProvider.php b/src/ApiDomain/External/Tag/src/ConfigProvider.php new file mode 100644 index 0000000..e624ab4 --- /dev/null +++ b/src/ApiDomain/External/Tag/src/ConfigProvider.php @@ -0,0 +1,16 @@ + require __DIR__ . './../config/service_manager.php', + 'routes' => require __DIR__ . '/./../config/routes.php', + ]; + } +} diff --git a/src/ApiDomain/External/Tag/src/Handler/AddAliasHandler.php b/src/ApiDomain/External/Tag/src/Handler/AddAliasHandler.php new file mode 100644 index 0000000..879809d --- /dev/null +++ b/src/ApiDomain/External/Tag/src/Handler/AddAliasHandler.php @@ -0,0 +1,44 @@ +getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $addAliasCommand = $this->addAliasCommandBuilder->build( + Uuid::fromString($data['tagId']), + $data['description'] + ); + $result = $this->addAliasCommandHandler->execute($addAliasCommand); + + return new SuccessResponse($this->responseFormatter->format($result)); + } +} diff --git a/src/ApiDomain/External/Tag/src/Handler/CreateHandler.php b/src/ApiDomain/External/Tag/src/Handler/CreateHandler.php new file mode 100644 index 0000000..20b4550 --- /dev/null +++ b/src/ApiDomain/External/Tag/src/Handler/CreateHandler.php @@ -0,0 +1,40 @@ +getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $createCommand = $this->createCommandBuilder->build( + $data['description'] + ); + $result = $this->createCommandHandler->execute($createCommand); + + return new SuccessResponse($this->responseFormatter->format($result)); + } +} diff --git a/src/ApiDomain/External/Tag/src/Handler/ReadDetailsHandler.php b/src/ApiDomain/External/Tag/src/Handler/ReadDetailsHandler.php new file mode 100644 index 0000000..1b6dc03 --- /dev/null +++ b/src/ApiDomain/External/Tag/src/Handler/ReadDetailsHandler.php @@ -0,0 +1,41 @@ +getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $readDetailsQuery = $this->readDetailsQueryBuilder->build( + Uuid::fromString($data['tagId']), + ); + $result = $this->readDetailsQueryHandler->execute($readDetailsQuery); + + return new SuccessResponse($this->responseFormatter->format($result)); + } +} diff --git a/src/ApiDomain/External/Tag/src/Handler/ReadVideoListHandler.php b/src/ApiDomain/External/Tag/src/Handler/ReadVideoListHandler.php new file mode 100644 index 0000000..3960aa7 --- /dev/null +++ b/src/ApiDomain/External/Tag/src/Handler/ReadVideoListHandler.php @@ -0,0 +1,40 @@ +getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $readVideoListQuery = $this->readVideoListQueryBuilder->build( + Uuid::fromString($data['tagId']), + $data['query'] ?? null, + $data['page'], + $data['perPage'], + ); + $result = $this->readVideoListQueryHandler->execute($readVideoListQuery); + + return new SuccessResponse($this->responseFormatter->format($result)); + } +} diff --git a/src/ApiDomain/External/Tag/src/ResponseFormatter/AddAliasResponseFormatter.php b/src/ApiDomain/External/Tag/src/ResponseFormatter/AddAliasResponseFormatter.php new file mode 100644 index 0000000..d405e83 --- /dev/null +++ b/src/ApiDomain/External/Tag/src/ResponseFormatter/AddAliasResponseFormatter.php @@ -0,0 +1,25 @@ +getAlias(); + $tag = $alias->getTag(); + + return [ + 'id' => $alias->getId()->toString(), + 'description' => $alias->getDescription(), + 'tag' => [ + 'id' => $tag->getId()->toString(), + 'description' => $tag->getDescription() + ], + ]; + } +} diff --git a/src/ApiDomain/External/Tag/src/ResponseFormatter/CreateResponseFormatter.php b/src/ApiDomain/External/Tag/src/ResponseFormatter/CreateResponseFormatter.php new file mode 100644 index 0000000..0b0a729 --- /dev/null +++ b/src/ApiDomain/External/Tag/src/ResponseFormatter/CreateResponseFormatter.php @@ -0,0 +1,19 @@ +getTag(); + return [ + 'id' => $tag->getId()->toString(), + 'description' => $tag->getDescription(), + ]; + } +} diff --git a/src/ApiDomain/External/Tag/src/ResponseFormatter/ReadDetailsResponseFormatter.php b/src/ApiDomain/External/Tag/src/ResponseFormatter/ReadDetailsResponseFormatter.php new file mode 100644 index 0000000..d15c4a6 --- /dev/null +++ b/src/ApiDomain/External/Tag/src/ResponseFormatter/ReadDetailsResponseFormatter.php @@ -0,0 +1,32 @@ +getTag(); + + $aliases = []; + + /** @var TagAlias $alias */ + foreach ($tag->getAliases() as $alias) { + $aliases[] = [ + 'id' => $alias->getId(), + 'description' => $alias->getDescription() + ]; + } + + return [ + 'id' => $tag->getId()->toString(), + 'description' => $tag->getDescription(), + 'aliases' => $aliases + ]; + } +} diff --git a/src/ApiDomain/External/Tag/src/ResponseFormatter/ReadVideoListResponseFormatter.php b/src/ApiDomain/External/Tag/src/ResponseFormatter/ReadVideoListResponseFormatter.php new file mode 100644 index 0000000..d93b426 --- /dev/null +++ b/src/ApiDomain/External/Tag/src/ResponseFormatter/ReadVideoListResponseFormatter.php @@ -0,0 +1,45 @@ +getPaginator(); + $videos = $paginator->getIterator(); + + $items = []; + + /** @var Video $video */ + foreach ($videos as $video) { + $tags = []; + + /** @var Tag $tag */ + foreach ($video->getTags() as $tag) { + $tags[] = [ + 'id' => $tag->getId(), + 'description' => $tag->getDescription() + ]; + } + + $items[] = [ + 'title' => $video->getTitle(), + 'id' => $video->getId(), + 'duration' => $video->getDuration() === null ? 'n. def' : gmdate("H:i:s", $video->getDuration()), + 'tags' => $tags + ]; + } + + return [ + 'total' => $paginator->count(), + 'items' => $items, + ]; + } +} diff --git a/src/ApiDomain/External/TagList/config/routes.php b/src/ApiDomain/External/TagList/config/routes.php new file mode 100644 index 0000000..a095ae2 --- /dev/null +++ b/src/ApiDomain/External/TagList/config/routes.php @@ -0,0 +1,16 @@ + 'tag-list.read-list', + 'path' => '/api/tag-list/read-list[/]', + 'allowed_methods' => ['POST'], + 'middleware' => [ + ReadListHandler::class, + ], + ], +]; diff --git a/src/ApiDomain/External/TagList/config/service_manager.php b/src/ApiDomain/External/TagList/config/service_manager.php new file mode 100644 index 0000000..2919fb9 --- /dev/null +++ b/src/ApiDomain/External/TagList/config/service_manager.php @@ -0,0 +1,17 @@ + [ + // Handler + ReadListHandler::class => AutoWiringFactory::class, + + // Response Formatter + ReadListResponseFormatter::class => AutoWiringFactory::class, + ], +]; diff --git a/src/ApiDomain/External/TagList/src/ConfigProvider.php b/src/ApiDomain/External/TagList/src/ConfigProvider.php new file mode 100644 index 0000000..4e42d1d --- /dev/null +++ b/src/ApiDomain/External/TagList/src/ConfigProvider.php @@ -0,0 +1,16 @@ + require __DIR__ . './../config/service_manager.php', + 'routes' => require __DIR__ . '/./../config/routes.php', + ]; + } +} diff --git a/src/ApiDomain/External/TagList/src/Handler/ReadListHandler.php b/src/ApiDomain/External/TagList/src/Handler/ReadListHandler.php new file mode 100644 index 0000000..77fbd5a --- /dev/null +++ b/src/ApiDomain/External/TagList/src/Handler/ReadListHandler.php @@ -0,0 +1,36 @@ +getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $readListQuery = $this->readListQueryBuilder->build( + $data['query'] ?? null, + ); + $result = $this->readListQueryHandler->execute($readListQuery); + + return new SuccessResponse($this->responseFormatter->format($result)); + } +} diff --git a/src/ApiDomain/External/TagList/src/ResponseFormatter/ReadListResponseFormatter.php b/src/ApiDomain/External/TagList/src/ResponseFormatter/ReadListResponseFormatter.php new file mode 100644 index 0000000..e443a70 --- /dev/null +++ b/src/ApiDomain/External/TagList/src/ResponseFormatter/ReadListResponseFormatter.php @@ -0,0 +1,31 @@ +getItems(); + $items = []; + + /** @var Tag $tag */ + foreach ($tags as $tag) { + $items[] = [ + 'id' => $tag->getId()->toString(), + 'description' => $tag->getDescription(), + 'videoCount' => $tag->getVideos()->count() + ]; + } + + return [ + 'total' => count($items), + 'items' => $items, + ]; + } +} diff --git a/src/ApiDomain/External/User/config/routes.php b/src/ApiDomain/External/User/config/routes.php new file mode 100644 index 0000000..6515800 --- /dev/null +++ b/src/ApiDomain/External/User/config/routes.php @@ -0,0 +1,48 @@ + 'user.create-user', + 'path' => '/api/user/create-user', + 'allowed_methods' => ['POST'], + 'middleware' => [ + LoggedInUserMiddleware::class, + EnsureAuthorizationMiddleware::class, + CreateUserHandler::class + ], + ], + [ + 'name' => 'user.change-password', + 'path' => '/api/user/change-password', + 'allowed_methods' => ['POST'], + 'middleware' => [ + LoggedInUserMiddleware::class, + ChangePasswordHandler::class + ], + ], + [ + 'name' => 'user.change-username', + 'path' => '/api/user/change-username', + 'allowed_methods' => ['POST'], + 'middleware' => [ + LoggedInUserMiddleware::class, + ChangeUsernameHandler::class + ], + ], + [ + 'name' => 'user.state', + 'path' => '/api/user/state', + 'allowed_methods' => ['GET'], + 'middleware' => [ + LoggedInUserMiddleware::class, + UserStateHandler::class + ], + ], +]; diff --git a/src/ApiDomain/External/User/config/service_manager.php b/src/ApiDomain/External/User/config/service_manager.php new file mode 100644 index 0000000..2808a41 --- /dev/null +++ b/src/ApiDomain/External/User/config/service_manager.php @@ -0,0 +1,21 @@ + [ + // Formatter + UserFormatter::class => AutoWiringFactory::class, + + // Handler + CreateUserHandler::class => AutoWiringFactory::class, + UserStateHandler::class => AutoWiringFactory::class, + ChangePasswordHandler::class => AutoWiringFactory::class, + ChangeUsernameHandler::class => AutoWiringFactory::class, + ], +]; diff --git a/src/ApiDomain/External/User/src/ConfigProvider.php b/src/ApiDomain/External/User/src/ConfigProvider.php new file mode 100644 index 0000000..4792e0c --- /dev/null +++ b/src/ApiDomain/External/User/src/ConfigProvider.php @@ -0,0 +1,16 @@ + require __DIR__ . '/./../config/service_manager.php', + 'routes' => require __DIR__ . '/./../config/routes.php', + ]; + } +} diff --git a/src/ApiDomain/External/User/src/Formatter/UserFormatter.php b/src/ApiDomain/External/User/src/Formatter/UserFormatter.php new file mode 100644 index 0000000..d9a3823 --- /dev/null +++ b/src/ApiDomain/External/User/src/Formatter/UserFormatter.php @@ -0,0 +1,27 @@ + $user->getId()->toString(), + 'username' => $user->getUsername(), + 'role' => $user->getRole()->getIdentifier(), + 'created' => $user->getCreatedAt()->format(DateTime::ATOM), + 'updated' => $user->getUpdatedAt()->format(DateTime::ATOM) + ]; + + $userArray['permissions'] = []; + + foreach ($user->getRole()->getPermissions()->toArray() as $permission) { + $userArray['permissions'][] = $permission->getIdentifier(); + } + + return $userArray; + } +} diff --git a/src/ApiDomain/External/User/src/Handler/ChangePasswordHandler.php b/src/ApiDomain/External/User/src/Handler/ChangePasswordHandler.php new file mode 100644 index 0000000..0e8c7ba --- /dev/null +++ b/src/ApiDomain/External/User/src/Handler/ChangePasswordHandler.php @@ -0,0 +1,40 @@ +getAttribute(LoggedInUserMiddleware::USER_KEY); + + $data = $request->getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $query = $this->builder->build( + $user, + $data['password'], + $data['newPassword'], + ); + $this->handler->execute($query); + + return new SuccessResponse(); + } +} diff --git a/src/ApiDomain/External/User/src/Handler/ChangeUsernameHandler.php b/src/ApiDomain/External/User/src/Handler/ChangeUsernameHandler.php new file mode 100644 index 0000000..f4498b5 --- /dev/null +++ b/src/ApiDomain/External/User/src/Handler/ChangeUsernameHandler.php @@ -0,0 +1,40 @@ +getAttribute(LoggedInUserMiddleware::USER_KEY); + $data = $request->getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $query = $this->builder->build( + $user, + $data['password'], + $data['newUsername'], + ); + $this->handler->execute($query); + + return new SuccessResponse(); + } +} diff --git a/src/ApiDomain/External/User/src/Handler/CreateUserHandler.php b/src/ApiDomain/External/User/src/Handler/CreateUserHandler.php new file mode 100644 index 0000000..b9a09a5 --- /dev/null +++ b/src/ApiDomain/External/User/src/Handler/CreateUserHandler.php @@ -0,0 +1,42 @@ +getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $query = $this->builder->build( + $data['username'], + $data['mail'], + $data['password'], + ); + $result = $this->handler->execute($query); + + return new JsonResponse( + $this->userFormatter->format($result) + ); + } +} + +?> \ No newline at end of file diff --git a/src/ApiDomain/External/User/src/Handler/UserStateHandler.php b/src/ApiDomain/External/User/src/Handler/UserStateHandler.php new file mode 100644 index 0000000..6f513ec --- /dev/null +++ b/src/ApiDomain/External/User/src/Handler/UserStateHandler.php @@ -0,0 +1,34 @@ +getAttribute(LoggedInUserMiddleware::USER_KEY); + + $response = $this->userFormatter->format($user); + $response['sessionId'] = $user->getSession()->getId()->toString(); + + return new JsonResponse($response); + } +} diff --git a/src/ApiDomain/External/Video/config/routes.php b/src/ApiDomain/External/Video/config/routes.php new file mode 100644 index 0000000..5ff06b4 --- /dev/null +++ b/src/ApiDomain/External/Video/config/routes.php @@ -0,0 +1,68 @@ + 'video.stream', + 'path' => '/api/video/stream/:videoUuid[/]', + 'allowed_methods' => ['GET'], + 'middleware' => [ + StreamHandler::class + ], + ], + [ + 'name' => 'video.thumbnail', + 'path' => '/api/video/thumbnail/:videoUuid[/]', + 'allowed_methods' => ['GET'], + 'middleware' => [ + ThumbnailHandler::class + ], + ], + [ + 'name' => 'video.upload', + 'path' => '/api/video/upload[/]', + 'allowed_methods' => ['POST'], + 'middleware' => [ + UploadHandler::class + ], + ], + [ + 'name' => 'video.read-details', + 'path' => '/api/video/read-details[/]', + 'allowed_methods' => ['POST'], + 'middleware' => [ + ReadDetailsHandler::class + ], + ], + [ + 'name' => 'video.set-thumbnail', + 'path' => '/api/video/set-thumbnail[/]', + 'allowed_methods' => ['POST'], + 'middleware' => [ + SetThumbnailHandler::class + ], + ], + [ + 'name' => 'video.update-details', + 'path' => '/api/video/update-details', + 'allowed_methods' => ['POST'], + 'middleware' => [ + UpdateDetailsHandler::class + ], + ], + [ + 'name' => 'video.delete', + 'path' => '/api/video/delete', + 'allowed_methods' => ['POST'], + 'middleware' => [ + DeleteHandler::class + ], + ], +]; diff --git a/src/ApiDomain/External/Video/config/service_manager.php b/src/ApiDomain/External/Video/config/service_manager.php new file mode 100644 index 0000000..40d6272 --- /dev/null +++ b/src/ApiDomain/External/Video/config/service_manager.php @@ -0,0 +1,31 @@ + [ + // Handler + ThumbnailHandler::class => AutoWiringFactory::class, + StreamHandler::class => AutoWiringFactory::class, + UploadHandler::class => AutoWiringFactory::class, + ReadDetailsHandler::class => AutoWiringFactory::class, + SetThumbnailHandler::class => AutoWiringFactory::class, + UpdateDetailsHandler::class => AutoWiringFactory::class, + DeleteHandler::class => AutoWiringFactory::class, + + // Response Formatter + ReadDetailsResponseFormatter::class => AutoWiringFactory::class, + SetThumbnailResponseFormatter::class => AutoWiringFactory::class, + UpdateDetailsResponseFormatter::class => AutoWiringFactory::class, + ], +]; diff --git a/src/ApiDomain/External/Video/src/ConfigProvider.php b/src/ApiDomain/External/Video/src/ConfigProvider.php new file mode 100644 index 0000000..be33f4a --- /dev/null +++ b/src/ApiDomain/External/Video/src/ConfigProvider.php @@ -0,0 +1,16 @@ + require __DIR__ . './../config/service_manager.php', + 'routes' => require __DIR__ . '/./../config/routes.php', + ]; + } +} diff --git a/src/ApiDomain/External/Video/src/Handler/DeleteHandler.php b/src/ApiDomain/External/Video/src/Handler/DeleteHandler.php new file mode 100644 index 0000000..30ef8ff --- /dev/null +++ b/src/ApiDomain/External/Video/src/Handler/DeleteHandler.php @@ -0,0 +1,36 @@ +getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $deleteCommand = $this->deleteCommandBuilder->build( + Uuid::fromString($data['videoId']) + ); + $result = $this->deleteCommandHandler->execute($deleteCommand); + + return new SuccessResponse(); + } +} diff --git a/src/ApiDomain/External/Video/src/Handler/ReadDetailsHandler.php b/src/ApiDomain/External/Video/src/Handler/ReadDetailsHandler.php new file mode 100644 index 0000000..198dd83 --- /dev/null +++ b/src/ApiDomain/External/Video/src/Handler/ReadDetailsHandler.php @@ -0,0 +1,41 @@ +getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $readDetailsQuery = $this->readDetailsQueryBuilder->build( + Uuid::fromString($data['videoId']) + ); + $result = $this->readDetailsQueryHandler->execute($readDetailsQuery); + + return new SuccessResponse($this->responseFormatter->format($result)); + } +} diff --git a/src/ApiDomain/External/Video/src/Handler/SetThumbnailHandler.php b/src/ApiDomain/External/Video/src/Handler/SetThumbnailHandler.php new file mode 100644 index 0000000..e2c73b9 --- /dev/null +++ b/src/ApiDomain/External/Video/src/Handler/SetThumbnailHandler.php @@ -0,0 +1,38 @@ +getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $setThumbnailCommand = $this->setThumbnailCommandBuilder->build( + Uuid::fromString($data['videoId']), + $data['timestamp'], + ); + $result = $this->setThumbnailCommandHandler->execute($setThumbnailCommand); + + return new SuccessResponse($this->responseFormatter->format($result)); + } +} diff --git a/src/ApiDomain/External/Video/src/Handler/StreamHandler.php b/src/ApiDomain/External/Video/src/Handler/StreamHandler.php new file mode 100644 index 0000000..0d6c473 --- /dev/null +++ b/src/ApiDomain/External/Video/src/Handler/StreamHandler.php @@ -0,0 +1,47 @@ +getAttribute('videoUuid') ?? null; + + if ($videoUuid === null) { + return new JsonResponse('No videoId provided'); + } + + $streamQuery = $this->streamQueryBuilder->build( + Uuid::fromString($videoUuid) + ); + $video = $this->streamQueryHandler->execute($streamQuery); + + $stream = new VideoStream(); + return $stream->streamVideo( + $video->getDirectoryPath() . 'video.mp4', + [ + 'is_localPath' => true, + 'content_type' => 'video/mp4' + ] + ); + } +} diff --git a/src/ApiDomain/External/Video/src/Handler/ThumbnailHandler.php b/src/ApiDomain/External/Video/src/Handler/ThumbnailHandler.php new file mode 100644 index 0000000..f44bbcc --- /dev/null +++ b/src/ApiDomain/External/Video/src/Handler/ThumbnailHandler.php @@ -0,0 +1,47 @@ +getAttribute('videoUuid') ?? null; + + if ($videoUuid === null) { + return new JsonResponse('No videoId provided'); + } + + $thumbnailQuery = $this->thumbnailQueryBuilder->build( + Uuid::fromString($videoUuid) + ); + $result = $this->thumbnailQueryHandler->execute($thumbnailQuery); + + $filePath = $result->getVideo()->getDirectoryPath() . 'thumbnail.png'; + + if (!file_exists($filePath)) { + return new JsonResponse('Not Found', 404); + } + + $fileContent = file_get_contents($filePath); + $response = new TextResponse($fileContent); + return $response->withHeader('Content-Type', 'image/png'); + } +} diff --git a/src/ApiDomain/External/Video/src/Handler/UpdateDetailsHandler.php b/src/ApiDomain/External/Video/src/Handler/UpdateDetailsHandler.php new file mode 100644 index 0000000..f51ead8 --- /dev/null +++ b/src/ApiDomain/External/Video/src/Handler/UpdateDetailsHandler.php @@ -0,0 +1,38 @@ +getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $updateDetailsCommand = $this->updateDetailsCommandBuilder->build( + Uuid::fromString($data['videoId']), + $data['title'] ?? null, + $data['tagIds'] ?? null, + ); + $result = $this->updateDetailsCommandHandler->execute($updateDetailsCommand); + + return new SuccessResponse(); + } +} diff --git a/src/ApiDomain/External/Video/src/Handler/UploadHandler.php b/src/ApiDomain/External/Video/src/Handler/UploadHandler.php new file mode 100644 index 0000000..e613840 --- /dev/null +++ b/src/ApiDomain/External/Video/src/Handler/UploadHandler.php @@ -0,0 +1,41 @@ +getUploadedFiles(); + + if (count($uploadedFiles) !== 1) { + return new JsonResponse('Uploaded more than one file', Response::STATUS_CODE_400); + } + + $uploadCommand = $this->uploadCommandBuilder->build( + $request->getUploadedFiles()['file'] + ); + $result = $this->uploadCommandHandler->execute($uploadCommand); + + return new SuccessResponse([ + 'id' => $result->getId()->toString() + ]); + } +} diff --git a/src/ApiDomain/External/Video/src/ResponseFormatter/DeleteResponseFormatter.php b/src/ApiDomain/External/Video/src/ResponseFormatter/DeleteResponseFormatter.php new file mode 100644 index 0000000..930a3f6 --- /dev/null +++ b/src/ApiDomain/External/Video/src/ResponseFormatter/DeleteResponseFormatter.php @@ -0,0 +1,15 @@ +getVideo(); + + $tags = []; + + /** @var Tag $tag */ + foreach ($video->getTags() as $tag) { + $tags[] = [ + 'id' => $tag->getId(), + 'description' => $tag->getDescription(), + ]; + } + + return [ + 'id' => $video->getId(), + 'title' => $video->getTitle(), + 'duration' => gmdate("H:i:s", $video->getDuration()), + 'tags' => $tags, + ]; + } +} diff --git a/src/ApiDomain/External/Video/src/ResponseFormatter/SetThumbnailResponseFormatter.php b/src/ApiDomain/External/Video/src/ResponseFormatter/SetThumbnailResponseFormatter.php new file mode 100644 index 0000000..7d69e44 --- /dev/null +++ b/src/ApiDomain/External/Video/src/ResponseFormatter/SetThumbnailResponseFormatter.php @@ -0,0 +1,15 @@ + 'video-list.read-list', + 'path' => '/api/video-list/read-list[/]', + 'allowed_methods' => ['POST'], + 'middleware' => [ + ReadListHandler::class + ], + ], + [ + 'name' => 'video-list.upload', + 'path' => '/api/video-list/upload[/]', + 'allowed_methods' => ['POST'], + 'middleware' => [ + UploadHandler::class + ], + ], +]; diff --git a/src/ApiDomain/External/VideoList/config/service_manager.php b/src/ApiDomain/External/VideoList/config/service_manager.php new file mode 100644 index 0000000..356dd8e --- /dev/null +++ b/src/ApiDomain/External/VideoList/config/service_manager.php @@ -0,0 +1,17 @@ + [ + ReadListHandler::class => AutoWiringFactory::class, + UploadHandler::class => AutoWiringFactory::class, + + ReadListResponseFormatter::class => AutoWiringFactory::class, + UploadResponseFormatter::class => AutoWiringFactory::class, + ], +]; diff --git a/src/ApiDomain/External/VideoList/src/ConfigProvider.php b/src/ApiDomain/External/VideoList/src/ConfigProvider.php new file mode 100644 index 0000000..15b7656 --- /dev/null +++ b/src/ApiDomain/External/VideoList/src/ConfigProvider.php @@ -0,0 +1,16 @@ + require __DIR__ . './../config/service_manager.php', + 'routes' => require __DIR__ . '/./../config/routes.php', + ]; + } +} diff --git a/src/ApiDomain/External/VideoList/src/Handler/ReadListHandler.php b/src/ApiDomain/External/VideoList/src/Handler/ReadListHandler.php new file mode 100644 index 0000000..832bfb6 --- /dev/null +++ b/src/ApiDomain/External/VideoList/src/Handler/ReadListHandler.php @@ -0,0 +1,39 @@ +getAttribute(AnalyzeBodyMiddleware::JSON_DATA); + + $readListQuery = $this->readListQueryBuilder->build( + $data['query'] ?? null, + $data['page'], + $data['perPage'], + $data['onlyTagless'] ?? false, + ); + $result = $this->readListQueryHandler->execute($readListQuery); + + return new SuccessResponse($this->readListResponseFormatter->format($result)); + } +} diff --git a/src/ApiDomain/External/VideoList/src/Handler/UploadHandler.php b/src/ApiDomain/External/VideoList/src/Handler/UploadHandler.php new file mode 100644 index 0000000..37461a1 --- /dev/null +++ b/src/ApiDomain/External/VideoList/src/Handler/UploadHandler.php @@ -0,0 +1,41 @@ +getUploadedFiles(); + + if (count($uploadedFiles) < 1) { + return new JsonResponse('Upload at least one File', Response::STATUS_CODE_400); + } + + $uploadCommand = $this->uploadCommandBuilder->build( + $request->getUploadedFiles() + ); + $result = $this->uploadCommandHandler->execute($uploadCommand); + + return new SuccessResponse($this->responseFormatter->format($result)); + } +} diff --git a/src/ApiDomain/External/VideoList/src/ResponseFormatter/ReadListResponseFormatter.php b/src/ApiDomain/External/VideoList/src/ResponseFormatter/ReadListResponseFormatter.php new file mode 100644 index 0000000..efeaf51 --- /dev/null +++ b/src/ApiDomain/External/VideoList/src/ResponseFormatter/ReadListResponseFormatter.php @@ -0,0 +1,45 @@ +getPaginator(); + $videos = $paginator->getIterator(); + + $items = []; + + /** @var Video $video */ + foreach ($videos as $video) { + $tags = []; + + /** @var Tag $tag */ + foreach ($video->getTags() as $tag) { + $tags[] = [ + 'id' => $tag->getId(), + 'description' => $tag->getDescription() + ]; + } + + $items[] = [ + 'title' => $video->getTitle(), + 'id' => $video->getId(), + 'duration' => $video->getDuration() === null ? 'n. def' : gmdate("H:i:s", $video->getDuration()), + 'tags' => $tags + ]; + } + + return [ + 'total' => $paginator->count(), + 'items' => $items, + ]; + } +} diff --git a/src/ApiDomain/External/VideoList/src/ResponseFormatter/UploadResponseFormatter.php b/src/ApiDomain/External/VideoList/src/ResponseFormatter/UploadResponseFormatter.php new file mode 100644 index 0000000..0286d8b --- /dev/null +++ b/src/ApiDomain/External/VideoList/src/ResponseFormatter/UploadResponseFormatter.php @@ -0,0 +1,46 @@ +getUploadedFileResults(); + $details = []; + + $successCount = 0; + $failCount = 0; + + /** @var UploadedFileResult $uploadedFileResult */ + foreach ($uploadedFileResults as $uploadedFileResult) { + + $detail = [ + 'file' => $uploadedFileResult->getUploadedFile()->getClientFilename(), + 'success' => $uploadedFileResult->getSuccess(), + ]; + + if ($uploadedFileResult->getSuccess()) { + $detail['id'] = $uploadedFileResult->getVideo()->getId()->toString(); + $successCount++; + } else { + $detail['error'] = $uploadedFileResult->getException()->getMessage(); + $failCount++; + } + + $details[] = $detail; + } + + return [ + 'total' => count($uploadedFileResults), + 'failed' => $failCount, + 'succeeded' => $successCount, + 'details' => $details + ]; + } +} diff --git a/src/DataDomain/Business/config/doctrine.php b/src/DataDomain/Business/config/doctrine.php new file mode 100644 index 0000000..2b0778f --- /dev/null +++ b/src/DataDomain/Business/config/doctrine.php @@ -0,0 +1,55 @@ + [ + 'orm_myTube' => [ + 'second_level_cache' => [ + 'enabled' => false, + ] + ], + ], + + 'driver' => [ + 'orm_myTube_annotation_driver' => [ + 'class' => AnnotationDriver::class, + 'cache' => 'array', + 'paths' => [ + realpath(APP_ROOT . '/src/DataDomain/Business/Entity/') + ], + ], + + 'orm_myTube' => [ + 'class' => MappingDriverChain::class, + 'drivers' => [ + 'MyTube\Data\Business\Entity' => 'orm_myTube_annotation_driver', + ], + ], + ], + + 'connection' => [ + 'orm_myTube' => [ + 'driverClass' => Driver::class, + 'params' => [ + 'driver' => $_ENV['DB_DRIVER'], + 'host' => $_ENV['DB_HOST'], + 'port' => $_ENV['DB_PORT'], + 'user' => $_ENV['DB_USER'], + 'password' => $_ENV['DB_PASSWORD'], + 'dbname' => $_ENV['DB_NAME'], + ], + ], + ], + + 'migrations_configuration' => [ + 'orm_myTube' => [ + 'directory' => 'data/migrations/myTube', + 'name' => 'Doctrine Database Migrations for MyTube', + 'namespace' => 'MyTube\Migrations\MyTube', + 'table' => 'migrations', + ], + ], +]; \ No newline at end of file diff --git a/src/DataDomain/Business/config/service_manager.php b/src/DataDomain/Business/config/service_manager.php new file mode 100644 index 0000000..46e48f6 --- /dev/null +++ b/src/DataDomain/Business/config/service_manager.php @@ -0,0 +1,21 @@ + [ + 'doctrine.entity_manager.orm_myTube' => [EntityManagerFactory::class, 'orm_myTube'], + 'doctrine.configuration.orm_myTube' => [ConfigurationFactory::class, 'orm_myTube'], + 'doctrine.connection.orm_myTube' => [ConnectionFactory::class, 'orm_myTube'], + MyTubeEntityManager::class => MyTubeEntityManagerFactory::class, + + UserRepository::class => [AutowireRepositoryFactory::class, MyTubeEntityManager::class, User::class], + ], +]; diff --git a/src/DataDomain/Business/src/ConfigProvider.php b/src/DataDomain/Business/src/ConfigProvider.php new file mode 100644 index 0000000..3b28200 --- /dev/null +++ b/src/DataDomain/Business/src/ConfigProvider.php @@ -0,0 +1,16 @@ + require __DIR__ . './../config/service_manager.php', + 'doctrine' => require __DIR__ . './../config/doctrine.php', + ]; + } +} diff --git a/src/DataDomain/Business/src/Entity/Permission.php b/src/DataDomain/Business/src/Entity/Permission.php new file mode 100644 index 0000000..5eb673d --- /dev/null +++ b/src/DataDomain/Business/src/Entity/Permission.php @@ -0,0 +1,75 @@ +id = UuidGenerator::generate(); + $this->roles = new ArrayCollection(); + } + + + public function getId(): UuidInterface { + return $this->id; + } + + public function getIdentifier(): string { + return $this->identifier; + } + + public function setIdentifier(string $identifier): void { + $this->identifier = $identifier; + } + + public function getRoles(): Collection { + return $this->roles; + } + + public function setRoles(Collection $roles): void { + $this->roles = $roles; + } + + public function addRole(Role $role): void { + if (!$this->roles->contains($role)) { + $this->roles->add($role); + $role->addPermission($this); + } + } + + public function removeRole(Role $role): void { + if ($this->roles->contains($role)) { + $this->roles->remove($role); + $role->removePermission($this); + } + } +} diff --git a/src/DataDomain/Business/src/Entity/Registration.php b/src/DataDomain/Business/src/Entity/Registration.php new file mode 100644 index 0000000..8b759dc --- /dev/null +++ b/src/DataDomain/Business/src/Entity/Registration.php @@ -0,0 +1,66 @@ +id = UuidGenerator::generate(); + + $now = new DateTime(); + $this->setCreatedAt($now); + } + + + public function getId(): UuidInterface { + return $this->id; + } + + public function getUsername(): string { + return $this->username; + } + + public function setUsername(string $username): void { + $this->username = $username; + } + + public function getMail(): ?string { + return $this->mail; + } + + public function setMail(?string $mail): void { + $this->mail = $mail; + } + + public function getCreatedAt(): DateTime { + return $this->createdAt; + } + + public function setCreatedAt(DateTime $createdAt): void { + $this->createdAt = $createdAt; + } +} diff --git a/src/DataDomain/Business/src/Entity/Role.php b/src/DataDomain/Business/src/Entity/Role.php new file mode 100644 index 0000000..49e0141 --- /dev/null +++ b/src/DataDomain/Business/src/Entity/Role.php @@ -0,0 +1,79 @@ +id = UuidGenerator::generate(); + $this->permissions = new ArrayCollection(); + } + + + public function getId(): UuidInterface { + return $this->id; + } + + public function getIdentifier(): string { + return $this->identifier; + } + + public function setIdentifier(string $identifier): void { + $this->identifier = $identifier; + } + + public function getPermissions(): Collection { + return $this->permissions; + } + + public function setPermissions(Collection $permissions): void { + $this->permissions = $permissions; + } + + public function addPermission(Permission $permission): void { + if (!$this->permissions->contains($permission)) { + $this->permissions->add($permission); + $permission->addRole($this); + } + } + + public function removePermission(Permission $permission): void { + if ($this->permissions->contains($permission)) { + $this->permissions->remove($permission); + $permission->removeRole($this); + } + } +} diff --git a/src/DataDomain/Business/src/Entity/Tag.php b/src/DataDomain/Business/src/Entity/Tag.php new file mode 100644 index 0000000..4b0aa22 --- /dev/null +++ b/src/DataDomain/Business/src/Entity/Tag.php @@ -0,0 +1,119 @@ +id = UuidGenerator::generate(); + + $this->aliases = new ArrayCollection(); + $this->videos = new ArrayCollection(); + + $now = new DateTime(); + $this->setCreatedAt($now); + $this->setUpdatedAt($now); + } + + public function getId(): UuidInterface { + return $this->id; + } + + public function getDescription(): string { + return $this->description; + } + public function setDescription(string $description): void { + $this->description = $description; + } + + public function getVideos(): Collection + { + return $this->videos; + } + public function setVideos(Collection $videos): void + { + $this->videos = $videos; + } + public function addVideo(Video $video): void + { + if (!$this->videos->contains($video)) { + $this->videos->add($video); + } + } + public function removeVideo(Video $video): void + { + if ($this->videos->contains($video)) { + $this->videos->removeElement($video); + } + } + + public function getAliases(): Collection + { + return $this->aliases; + } + public function setAliases(Collection $aliases): void + { + $this->aliases = $aliases; + } + public function addAlias(TagAlias $alias): void + { + if (!$this->aliases->contains($alias)) { + $this->aliases->add($alias); + } + } + public function removeAlias(TagAlias $alias): void + { + if ($this->aliases->contains($alias)) { + $this->aliases->removeElement($alias); + } + } + + public function getCreatedAt(): DateTime { + return $this->createdAt; + } + public function setCreatedAt(DateTime $createdAt): void { + $this->createdAt = $createdAt; + } + + public function getUpdatedAt(): DateTime { + return $this->updatedAt; + } + public function setUpdatedAt(DateTime $updatedAt): void { + $this->updatedAt = $updatedAt; + } +} diff --git a/src/DataDomain/Business/src/Entity/TagAlias.php b/src/DataDomain/Business/src/Entity/TagAlias.php new file mode 100644 index 0000000..9cc0a28 --- /dev/null +++ b/src/DataDomain/Business/src/Entity/TagAlias.php @@ -0,0 +1,90 @@ +id = UuidGenerator::generate(); + + $now = new DateTime(); + $this->setCreatedAt($now); + $this->setUpdatedAt($now); + } + + public function getId(): UuidInterface { + return $this->id; + } + + public function getTagId(): UuidInterface + { + return $this->tagId; + } + public function setTagId(UuidInterface $tagId): void + { + $this->tagId = $tagId; + } + + public function getTag(): Tag + { + return $this->tag; + } + public function setTag(Tag $tag): void + { + $this->tag = $tag; + } + + public function getDescription(): string { + return $this->description; + } + public function setDescription(string $description): void { + $this->description = $description; + } + + public function getCreatedAt(): DateTime { + return $this->createdAt; + } + public function setCreatedAt(DateTime $createdAt): void { + $this->createdAt = $createdAt; + } + + public function getUpdatedAt(): DateTime { + return $this->updatedAt; + } + public function setUpdatedAt(DateTime $updatedAt): void { + $this->updatedAt = $updatedAt; + } +} diff --git a/src/DataDomain/Business/src/Entity/User.php b/src/DataDomain/Business/src/Entity/User.php new file mode 100644 index 0000000..0b54f0a --- /dev/null +++ b/src/DataDomain/Business/src/Entity/User.php @@ -0,0 +1,148 @@ +id = UuidGenerator::generate(); + + $now = new DateTime(); + $this->setCreatedAt($now); + $this->setUpdatedAt($now); + } + + + /** + * @ORM\PrePersist + * @ORM\PreUpdate + */ + public function updateTimestamps(): void { + $now = new DateTime(); + $this->setUpdatedAt($now); + } + + + public function getId(): UuidInterface { + return $this->id; + } + + public function getRoleId(): UuidInterface { + return $this->roleId; + } + + public function setRoleId(UuidInterface $roleId): void { + $this->roleId = $roleId; + } + + public function getRole(): Role { + return $this->role; + } + + public function setRole(Role $role): void { + $this->role = $role; + } + + public function getUsername(): string { + return $this->username; + } + + public function setUsername(string $username): void { + $this->username = $username; + } + + public function getMail(): ?string { + return $this->mail; + } + + public function setMail(?string $mail): void { + $this->mail = $mail; + } + + public function getPassword(): ?string { + return $this->password; + } + + public function setPassword(?string $password): void { + $this->password = $password; + } + + public function getSession(): ?UserSession { + return $this->session; + } + + public function setSession(?UserSession $session): void { + $this->session = $session; + } + + public function getLastLoginAt(): ?DateTime { + return $this->lastLoginAt; + } + + public function setLastLoginAt(?DateTime $lastLoginAt): void { + $this->lastLoginAt = $lastLoginAt; + } + + public function getCreatedAt(): DateTime { + return $this->createdAt; + } + + public function setCreatedAt(DateTime $createdAt): void { + $this->createdAt = $createdAt; + } + + public function getUpdatedAt(): DateTime { + return $this->updatedAt; + } + + public function setUpdatedAt(DateTime $updatedAt): void { + $this->updatedAt = $updatedAt; + } +} + +?> \ No newline at end of file diff --git a/src/DataDomain/Business/src/Entity/UserSession.php b/src/DataDomain/Business/src/Entity/UserSession.php new file mode 100644 index 0000000..bd23116 --- /dev/null +++ b/src/DataDomain/Business/src/Entity/UserSession.php @@ -0,0 +1,105 @@ +id = UuidGenerator::generate(); + + $now = new DateTime(); + $this->setCreatedAt($now); + $this->setUpdatedAt($now); + } + + + /** + * @ORM\PrePersist + * @ORM\PreUpdate + */ + public function updateTimestamps(): void { + $now = new DateTime(); + $this->setUpdatedAt($now); + } + + + public function getId(): UuidInterface { + return $this->id; + } + + public function getUserId(): ?UuidInterface { + return $this->userId; + } + + public function setUserId(?UuidInterface $userId): void { + $this->userId = $userId; + } + + public function getUser(): ?User { + return $this->user; + } + + public function setUser(?User $user): void { + $this->user = $user; + } + + public function getCsrf(): ?UuidInterface { + return $this->csrf; + } + + public function setCsrf(?UuidInterface $csrf): void { + $this->csrf = $csrf; + } + + + public function getCreatedAt(): DateTime { + return $this->createdAt; + } + + public function setCreatedAt(DateTime $createdAt): void { + $this->createdAt = $createdAt; + } + + public function getUpdatedAt(): DateTime { + return $this->updatedAt; + } + + public function setUpdatedAt(DateTime $updatedAt): void { + $this->updatedAt = $updatedAt; + } +} + +?> \ No newline at end of file diff --git a/src/DataDomain/Business/src/Entity/Video.php b/src/DataDomain/Business/src/Entity/Video.php new file mode 100644 index 0000000..a8f5d76 --- /dev/null +++ b/src/DataDomain/Business/src/Entity/Video.php @@ -0,0 +1,130 @@ +id = UuidGenerator::generate(); + + $this->tags = new ArrayCollection(); + $this->events = new ArrayCollection(); + + $now = new DateTime(); + $this->setCreatedAt($now); + $this->setUpdatedAt($now); + } + + + public function getId(): UuidInterface { + return $this->id; + } + + public function getTitle(): string { + return $this->title; + } + public function setTitle(string $title): void { + $this->title = $title; + } + + public function getDuration(): ?int + { + return $this->duration; + } + public function setDuration(?int $duration): void + { + $this->duration = $duration; + } + + public function getDirectoryPath(): ?string { + return $this->directoryPath; + } + public function setDirectoryPath(?string $directoryPath): void { + $this->directoryPath = $directoryPath; + } + + public function getTags(): Collection + { + return $this->tags; + } + public function setTags(Collection $tags): void + { + $this->tags = $tags; + } + public function addTag(Tag $tag): void + { + if (!$this->tags->contains($tag)) { + $this->tags->add($tag); + } + } + public function removeTag(Tag $tag): void + { + if ($this->tags->contains($tag)) { + $this->tags->removeElement($tag); + } + } + + public function getEvents(): Collection + { + return $this->events; + } + public function setEvents(Collection $events): void + { + $this->events = $events; + } + + public function getCreatedAt(): DateTime { + return $this->createdAt; + } + public function setCreatedAt(DateTime $createdAt): void { + $this->createdAt = $createdAt; + } + + public function getUpdatedAt(): DateTime { + return $this->updatedAt; + } + public function setUpdatedAt(DateTime $updatedAt): void { + $this->updatedAt = $updatedAt; + } +} diff --git a/src/DataDomain/Business/src/Entity/VideoEvent.php b/src/DataDomain/Business/src/Entity/VideoEvent.php new file mode 100644 index 0000000..0b0a8dc --- /dev/null +++ b/src/DataDomain/Business/src/Entity/VideoEvent.php @@ -0,0 +1,84 @@ +id = UuidGenerator::generate(); + + $this->tags = new ArrayCollection(); + + $now = new DateTime(); + $this->setDateTime($now); + } + + + public function getId(): UuidInterface { + return $this->id; + } + + public function getVideoId(): UuidInterface + { + return $this->videoId; + } + public function setVideoId(UuidInterface $videoId): void + { + $this->videoId = $videoId; + } + + public function getVideo(): Video + { + return $this->video; + } + public function setVideo(Video $video): void + { + $this->video = $video; + } + + public function getType(): string { + return $this->type; + } + public function setType(string $type): void { + $this->type = $type; + } + + public function getDateTime(): DateTime { + return $this->dateTime; + } + public function setDateTime(DateTime $dateTime): void { + $this->dateTime = $dateTime; + } +} diff --git a/src/DataDomain/Business/src/Factory/MyTubeEntityManagerFactory.php b/src/DataDomain/Business/src/Factory/MyTubeEntityManagerFactory.php new file mode 100644 index 0000000..6dcff24 --- /dev/null +++ b/src/DataDomain/Business/src/Factory/MyTubeEntityManagerFactory.php @@ -0,0 +1,19 @@ +get('doctrine.entity_manager.orm_myTube') + ); + } +} diff --git a/src/DataDomain/Business/src/Manager/MyTubeEntityManager.php b/src/DataDomain/Business/src/Manager/MyTubeEntityManager.php new file mode 100644 index 0000000..48529b8 --- /dev/null +++ b/src/DataDomain/Business/src/Manager/MyTubeEntityManager.php @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/src/DataDomain/Business/src/Repository/ProductRepository.php b/src/DataDomain/Business/src/Repository/ProductRepository.php new file mode 100644 index 0000000..bebda81 --- /dev/null +++ b/src/DataDomain/Business/src/Repository/ProductRepository.php @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/src/DataDomain/Business/src/Repository/RegistrationRepository.php b/src/DataDomain/Business/src/Repository/RegistrationRepository.php new file mode 100644 index 0000000..737519a --- /dev/null +++ b/src/DataDomain/Business/src/Repository/RegistrationRepository.php @@ -0,0 +1,24 @@ +createQueryBuilder('r'); + $queryBuilder->where( + $queryBuilder->expr()->orX( + $queryBuilder->expr()->eq('r.username', ':identifier'), + $queryBuilder->expr()->eq('r.mail', ':identifier') + ) + ) + ->setParameter('identifier', $identifier); + $query = $queryBuilder->getQuery(); + + /** @var ?Registration $registration */ + $registration = $query->execute()[0] ?? null; + return $registration; + } +} \ No newline at end of file diff --git a/src/DataDomain/Business/src/Repository/RoleRepository.php b/src/DataDomain/Business/src/Repository/RoleRepository.php new file mode 100644 index 0000000..95f89b7 --- /dev/null +++ b/src/DataDomain/Business/src/Repository/RoleRepository.php @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/src/DataDomain/Business/src/Repository/TagAliasRepository.php b/src/DataDomain/Business/src/Repository/TagAliasRepository.php new file mode 100644 index 0000000..2be07a0 --- /dev/null +++ b/src/DataDomain/Business/src/Repository/TagAliasRepository.php @@ -0,0 +1,8 @@ +createQueryBuilder('t'); + + if ($query !== null) { + $query = '%'.$query.'%'; + + $queryBuilder + ->where('t.description like :query') + ->setParameter('query', $query); + } + + return $queryBuilder->getQuery()->execute(); + } +} \ No newline at end of file diff --git a/src/DataDomain/Business/src/Repository/UserRepository.php b/src/DataDomain/Business/src/Repository/UserRepository.php new file mode 100644 index 0000000..768a596 --- /dev/null +++ b/src/DataDomain/Business/src/Repository/UserRepository.php @@ -0,0 +1,26 @@ +createQueryBuilder('u'); + $queryBuilder->where( + $queryBuilder->expr()->orX( + $queryBuilder->expr()->eq('u.username', ':identifier'), + $queryBuilder->expr()->eq('u.mail', ':identifier') + ) + ) + ->setParameter('identifier', $identifier); + $query = $queryBuilder->getQuery(); + + /** @var ?User $user */ + $user = $query->execute()[0] ?? null; + return $user; + } +} + +?> \ No newline at end of file diff --git a/src/DataDomain/Business/src/Repository/UserSessionRepository.php b/src/DataDomain/Business/src/Repository/UserSessionRepository.php new file mode 100644 index 0000000..f1b7bc9 --- /dev/null +++ b/src/DataDomain/Business/src/Repository/UserSessionRepository.php @@ -0,0 +1,22 @@ +createQueryBuilder('us'); + $queryBuilder + ->where("us.userId = :userId") + ->setParameter('userId', $user->getId()); + + /** @var ?UserSession $userSession */ + $userSession = $queryBuilder->getQuery()->execute()[0] ?? null; + return $userSession; + } +} + +?> \ No newline at end of file diff --git a/src/DataDomain/Business/src/Repository/VideoEventRepository.php b/src/DataDomain/Business/src/Repository/VideoEventRepository.php new file mode 100644 index 0000000..7659e69 --- /dev/null +++ b/src/DataDomain/Business/src/Repository/VideoEventRepository.php @@ -0,0 +1,8 @@ + 'v.duration', + 'title' => 'v.title', + 'createdAt' => 'v.createdAt' + ]; + + public function findByFilter( + ?string $query, + int $page, + int $perPage, + bool $onlyTagless, + ?Tag $tag = null, + ?string $orderBy = null, + string $orderDirection = 'desc' + ): Paginator + { + $orderBy = self::FIELD_MAP[$orderBy] ?? self::FIELD_MAP['createdAt']; + + $queryBuilder = $this->createQueryBuilder('v') + ->leftJoin('v.tags', 't'); + + if ($query !== null) { + $query = '%'.$query.'%'; + $queryBuilder = $queryBuilder + ->where('v.title like :query') + ->orWhere('t.description like :query') + ->setParameter('query', $query); + } + + if ($tag !== null) { + $queryBuilder = $queryBuilder + ->andWhere('t.id = :tagId') + ->setParameter('tagId', $tag->getId(), UuidBinaryOrderedTimeType::NAME); + } + + if ($onlyTagless) { + $queryBuilder = $queryBuilder + ->andWhere( $queryBuilder->expr()->isNull('t.id') ); + } + + $queryBuilder->orderBy($orderBy,$orderDirection); + + $queryBuilder->setFirstResult($perPage * ($page - 1)); + $queryBuilder->setMaxResults($perPage); + + return new Paginator($queryBuilder->getQuery()); + } +} \ No newline at end of file diff --git a/src/DataDomain/Log/config/doctrine.php b/src/DataDomain/Log/config/doctrine.php new file mode 100644 index 0000000..64e1e6f --- /dev/null +++ b/src/DataDomain/Log/config/doctrine.php @@ -0,0 +1,55 @@ + [ + 'orm_log' => [ + 'second_level_cache' => [ + 'enabled' => false, + ] + ], + ], + + 'driver' => [ + 'orm_log_annotation_driver' => [ + 'class' => AnnotationDriver::class, + 'cache' => 'array', + 'paths' => [ + realpath(APP_ROOT . '/src/DataDomain/Log/Entity/') + ], + ], + + 'orm_log' => [ + 'class' => MappingDriverChain::class, + 'drivers' => [ + 'MyTube\Data\Log\Entity' => 'orm_log_annotation_driver', + ], + ], + ], + + 'connection' => [ + 'orm_log' => [ + 'driverClass' => Driver::class, + 'params' => [ + 'driver' => $_ENV['DB_DRIVER'], + 'host' => $_ENV['DB_HOST'], + 'port' => $_ENV['DB_PORT'], + 'user' => $_ENV['DB_USER'], + 'password' => $_ENV['DB_PASSWORD'], + 'dbname' => $_ENV['DB_NAME_LOG'], + ], + ], + ], + + 'migrations_configuration' => [ + 'orm_log' => [ + 'directory' => 'data/migrations/log', + 'name' => 'Doctrine Database Migrations for Log', + 'namespace' => 'MyTube\Migrations\Log', + 'table' => 'migrations', + ], + ], +]; \ No newline at end of file diff --git a/src/DataDomain/Log/config/service_manager.php b/src/DataDomain/Log/config/service_manager.php new file mode 100644 index 0000000..3eb42ce --- /dev/null +++ b/src/DataDomain/Log/config/service_manager.php @@ -0,0 +1,16 @@ + [ + 'doctrine.entity_manager.orm_log' => [EntityManagerFactory::class, 'orm_log'], + 'doctrine.configuration.orm_log' => [ConfigurationFactory::class, 'orm_log'], + 'doctrine.connection.orm_log' => [ConnectionFactory::class, 'orm_log'], + LogEntityManager::class => LogEntityManagerFactory::class, + ], +]; diff --git a/src/DataDomain/Log/src/ConfigProvider.php b/src/DataDomain/Log/src/ConfigProvider.php new file mode 100644 index 0000000..17084e4 --- /dev/null +++ b/src/DataDomain/Log/src/ConfigProvider.php @@ -0,0 +1,16 @@ + require __DIR__ . './../config/service_manager.php', + 'doctrine' => require __DIR__ . './../config/doctrine.php', + ]; + } +} diff --git a/src/DataDomain/Log/src/Entity/Log.php b/src/DataDomain/Log/src/Entity/Log.php new file mode 100644 index 0000000..6119b71 --- /dev/null +++ b/src/DataDomain/Log/src/Entity/Log.php @@ -0,0 +1,129 @@ +id = UuidGenerator::generate(); + } + + + public function getId(): UuidInterface + { + return $this->id; + } + + public function setId(UuidInterface $id): void + { + $this->id = $id; + } + + public function getMessage(): string + { + return $this->message; + } + + public function setMessage(string $message): void + { + $this->message = $message; + } + + public function getContext(): ?array + { + return $this->context; + } + + public function setContext(?array $context): void + { + $this->context = $context; + } + + public function getLevel(): int + { + return $this->level; + } + + public function setLevel(int $level): void + { + $this->level = $level; + } + + public function getLevelName(): string + { + return $this->levelName; + } + + public function setLevelName(string $levelName): void + { + $this->levelName = $levelName; + } + + public function getExtra(): ?array + { + return $this->extra; + } + + public function setExtra(?array $extra): void + { + $this->extra = $extra; + } + + public function getCreatedAt(): DateTime + { + return $this->createdAt; + } + + public function setCreatedAt(DateTime $createdAt): void + { + $this->createdAt = $createdAt; + } +} diff --git a/src/DataDomain/Log/src/Factory/LogEntityManagerFactory.php b/src/DataDomain/Log/src/Factory/LogEntityManagerFactory.php new file mode 100644 index 0000000..c2b7657 --- /dev/null +++ b/src/DataDomain/Log/src/Factory/LogEntityManagerFactory.php @@ -0,0 +1,21 @@ +get('doctrine.entity_manager.orm_log') + ); + + return $lem; + } +} diff --git a/src/DataDomain/Log/src/Manager/LogEntityManager.php b/src/DataDomain/Log/src/Manager/LogEntityManager.php new file mode 100644 index 0000000..04b0f2d --- /dev/null +++ b/src/DataDomain/Log/src/Manager/LogEntityManager.php @@ -0,0 +1,11 @@ + [ + /// Builder + RegistrationBuilder::class => AutoWiringFactory::class, + + /// Rule + RegistrationWithIdentifierAlreadyExistsRule::class => InjectionFactory::class, + + /// Pipeline + // Register User + RegisterUserPipeline::class => AutoWiringFactory::class, + CheckIdentifierStep::class => AutoWiringFactory::class, + BuildRegistrationStep::class => AutoWiringFactory::class, + SendMailStep::class => AutoWiringFactory::class, + SaveRegistrationStep::class => AutoWiringFactory::class, + + // Confirm Registration + ConfirmRegistrationPipeline::class => AutoWiringFactory::class, + LoadRegistrationStep::class => InjectionFactory::class, + CreateUserStep::class => AutoWiringFactory::class, + SaveRegistrationAndUserStep::class => AutoWiringFactory::class, + + /// CQRS + // Register User + RegisterUserCommandHandler::class => AutoWiringFactory::class, + RegisterUserCommandBuilder::class => AutoWiringFactory::class, + + // Confirm Registration + ConfirmRegistrationCommandHandler::class => AutoWiringFactory::class, + ConfirmRegistrationCommandBuilder::class => AutoWiringFactory::class, + ], +]; diff --git a/src/HandlingDomain/Registration/src/Builder/RegistrationBuilder.php b/src/HandlingDomain/Registration/src/Builder/RegistrationBuilder.php new file mode 100644 index 0000000..5a759f9 --- /dev/null +++ b/src/HandlingDomain/Registration/src/Builder/RegistrationBuilder.php @@ -0,0 +1,19 @@ +setUsername($username); + $registration->setMail($mail); + return $registration; + } +} diff --git a/src/HandlingDomain/Registration/src/ConfigProvider.php b/src/HandlingDomain/Registration/src/ConfigProvider.php new file mode 100644 index 0000000..e5bcb13 --- /dev/null +++ b/src/HandlingDomain/Registration/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . '/./../config/service_manager.php', + ]; + } +} diff --git a/src/HandlingDomain/Registration/src/Exception/RegistrationNotFoundByIdException.php b/src/HandlingDomain/Registration/src/Exception/RegistrationNotFoundByIdException.php new file mode 100644 index 0000000..bdb86eb --- /dev/null +++ b/src/HandlingDomain/Registration/src/Exception/RegistrationNotFoundByIdException.php @@ -0,0 +1,25 @@ +toString() + ), + ErrorDomain::Registration, + ErrorCode::NotFound + ); + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Registration/src/Exception/RegistrationWithIdentifierAlreadyExistsException.php b/src/HandlingDomain/Registration/src/Exception/RegistrationWithIdentifierAlreadyExistsException.php new file mode 100644 index 0000000..85e283a --- /dev/null +++ b/src/HandlingDomain/Registration/src/Exception/RegistrationWithIdentifierAlreadyExistsException.php @@ -0,0 +1,25 @@ +id; + } + + public function getPassword(): string { + return $this->password; + } + + + public function getPasswordConfirmation(): string { + return $this->passwordConfirmation; + } +} diff --git a/src/HandlingDomain/Registration/src/Handler/Command/ConfirmRegistration/ConfirmRegistrationCommandBuilder.php b/src/HandlingDomain/Registration/src/Handler/Command/ConfirmRegistration/ConfirmRegistrationCommandBuilder.php new file mode 100644 index 0000000..e136f8f --- /dev/null +++ b/src/HandlingDomain/Registration/src/Handler/Command/ConfirmRegistration/ConfirmRegistrationCommandBuilder.php @@ -0,0 +1,21 @@ +pipeline->handle($payload); + + return $payload->getUser(); + } +} diff --git a/src/HandlingDomain/Registration/src/Handler/Command/RegisterUser/RegisterUserCommand.php b/src/HandlingDomain/Registration/src/Handler/Command/RegisterUser/RegisterUserCommand.php new file mode 100644 index 0000000..c388b88 --- /dev/null +++ b/src/HandlingDomain/Registration/src/Handler/Command/RegisterUser/RegisterUserCommand.php @@ -0,0 +1,25 @@ +username; + } + + public function getMail(): string { + return $this->mail; + } + + public function getHost(): string { + return $this->host; + } +} diff --git a/src/HandlingDomain/Registration/src/Handler/Command/RegisterUser/RegisterUserCommandBuilder.php b/src/HandlingDomain/Registration/src/Handler/Command/RegisterUser/RegisterUserCommandBuilder.php new file mode 100644 index 0000000..2ad0642 --- /dev/null +++ b/src/HandlingDomain/Registration/src/Handler/Command/RegisterUser/RegisterUserCommandBuilder.php @@ -0,0 +1,19 @@ +pipeline->handle($payload); + + return $payload->getRegistration(); + } +} diff --git a/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/ConfirmRegistrationPayload.php b/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/ConfirmRegistrationPayload.php new file mode 100644 index 0000000..05bd5c7 --- /dev/null +++ b/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/ConfirmRegistrationPayload.php @@ -0,0 +1,46 @@ +command; + } + + public function getRegistration(): ?Registration + { + return $this->registration; + } + + public function setRegistration(?Registration $registration) + { + $this->registration = $registration; + } + + public function getUser(): ?User + { + return $this->user; + } + + public function setUser(?User $user) + { + $this->user = $user; + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/ConfirmRegistrationPipeline.php b/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/ConfirmRegistrationPipeline.php new file mode 100644 index 0000000..98f85f6 --- /dev/null +++ b/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/ConfirmRegistrationPipeline.php @@ -0,0 +1,26 @@ +loadRegistrationStep, + $this->createUserStep, + $this->saveRegistrationStep + ]); + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/Step/CreateUserStep.php b/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/Step/CreateUserStep.php new file mode 100644 index 0000000..6e79d10 --- /dev/null +++ b/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/Step/CreateUserStep.php @@ -0,0 +1,60 @@ +getCommand(); + $registration = $payload->getRegistration(); + + if ( + !$this->passwordMatchRule->appliesTo( + $command->getPassword(), + $command->getPasswordConfirmation() + ) + ) { + throw new UserPasswordMismatchException(); + } + + $createUserCommand = $this->createUserCommandBuilder->build( + username: $registration->getUsername(), + mail: $registration->getMail(), + password: $command->getPassword(), + ); + $user = $this->createUserCommandHandler->execute($createUserCommand); + + $payload->setUser($user); + + $pipeline->next()($payload, $pipeline); + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/Step/LoadRegistrationStep.php b/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/Step/LoadRegistrationStep.php new file mode 100644 index 0000000..109af4e --- /dev/null +++ b/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/Step/LoadRegistrationStep.php @@ -0,0 +1,47 @@ +getCommand(); + + /** @var Registration $registration */ + $registration = $this->registrationRepository->findOneBy([ + 'id' => $command->getId() + ]) ?? null; + + if ($registration === null) { + throw new RegistrationNotFoundByIdException($command->getId()); + } + + $payload->setRegistration($registration); + + $pipeline->next()($payload, $pipeline); + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/Step/SaveRegistrationAndUserStep.php b/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/Step/SaveRegistrationAndUserStep.php new file mode 100644 index 0000000..9b52d88 --- /dev/null +++ b/src/HandlingDomain/Registration/src/Pipeline/ConfirmRegistration/Step/SaveRegistrationAndUserStep.php @@ -0,0 +1,32 @@ +getRegistration(); + $user = $payload->getUser(); + + $this->entityManager->remove($registration); + $this->entityManager->persist($user); + $this->entityManager->flush(); + + return $payload; + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/RegisterUserPayload.php b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/RegisterUserPayload.php new file mode 100644 index 0000000..2cdd71a --- /dev/null +++ b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/RegisterUserPayload.php @@ -0,0 +1,33 @@ +command; + } + + public function getRegistration(): ?Registration + { + return $this->registration; + } + + public function setRegistration(?Registration $registration) + { + $this->registration = $registration; + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/RegisterUserPipeline.php b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/RegisterUserPipeline.php new file mode 100644 index 0000000..9e3e0c5 --- /dev/null +++ b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/RegisterUserPipeline.php @@ -0,0 +1,29 @@ +checkIdentifierStep, + $this->buildRegistrationStep, + $this->sendMailStep, + $this->saveRegistrationStep + ]); + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/BuildRegistrationStep.php b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/BuildRegistrationStep.php new file mode 100644 index 0000000..2003050 --- /dev/null +++ b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/BuildRegistrationStep.php @@ -0,0 +1,34 @@ +getCommand(); + + $registration = $this->builder->build( + username: $command->getUsername(), + mail: $command->getMail() + ); + + $payload->setRegistration($registration); + + $pipeline->next()($payload, $pipeline); + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/CheckIdentifierStep.php b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/CheckIdentifierStep.php new file mode 100644 index 0000000..fddfb1d --- /dev/null +++ b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/CheckIdentifierStep.php @@ -0,0 +1,41 @@ +getCommand(); + + $this->userWithIdentifierAlreadyExistsRule->appliesTo($command->getMail()); + $this->userWithIdentifierAlreadyExistsRule->appliesTo($command->getUsername()); + + $this->registrationWithIdentifierAlreadyExistsRule->appliesTo($command->getMail()); + $this->registrationWithIdentifierAlreadyExistsRule->appliesTo($command->getUsername()); + + $pipeline->next()($payload, $pipeline); + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/SaveRegistrationStep.php b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/SaveRegistrationStep.php new file mode 100644 index 0000000..a934e56 --- /dev/null +++ b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/SaveRegistrationStep.php @@ -0,0 +1,30 @@ +getRegistration(); + + $this->entityManager->persist($registration); + $this->entityManager->flush(); + + return $payload; + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/SendMailStep.php b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/SendMailStep.php new file mode 100644 index 0000000..532cf80 --- /dev/null +++ b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/SendMailStep.php @@ -0,0 +1,49 @@ +getCommand(); + $registration = $payload->getRegistration(); + + $this->requestService->request( + 'notification', + 'send-mail', + [ + 'myTube-identifier' => 'new-account', + 'sender' => 'info@stack-up.de', + 'recipient' => $command->getMail(), + 'data' => [ + 'username' => $command->getUsername(), + 'confirmationLink' => + sprintf( + self::CONFIRM_LINK, + $command->getHost(), + $registration->getId()->toString() + ) + ] + ] + ); + + $pipeline->next()($payload, $pipeline); + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Registration/src/Rule/RegistrationWithIdentifierAlreadyExistsRule.php b/src/HandlingDomain/Registration/src/Rule/RegistrationWithIdentifierAlreadyExistsRule.php new file mode 100644 index 0000000..3f57b7b --- /dev/null +++ b/src/HandlingDomain/Registration/src/Rule/RegistrationWithIdentifierAlreadyExistsRule.php @@ -0,0 +1,29 @@ +registrationRepository->findByIdentifier($identifier); + + if ($registration !== null) { + throw new RegistrationWithIdentifierAlreadyExistsException($identifier); + } + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Role/src/Exception/RoleNotFoundByIdentifierException.php b/src/HandlingDomain/Role/src/Exception/RoleNotFoundByIdentifierException.php new file mode 100644 index 0000000..8ad8bb1 --- /dev/null +++ b/src/HandlingDomain/Role/src/Exception/RoleNotFoundByIdentifierException.php @@ -0,0 +1,24 @@ + [ + + /// Rule + TagExistsRule::class => InjectionFactory::class, + TagAliasExistsRule::class => InjectionFactory::class, + + /// Builder + TagBuilder::class => AutoWiringFactory::class, + TagAliasBuilder::class => AutoWiringFactory::class, + + /// CQRS + // Add Alias + AddAliasCommandBuilder::class => AutoWiringFactory::class, + AddAliasCommandHandler::class => InjectionFactory::class, + // Create + CreateCommandBuilder::class => AutoWiringFactory::class, + CreateCommandHandler::class => AutoWiringFactory::class, + // Read Details + ReadDetailsQueryBuilder::class => AutoWiringFactory::class, + ReadDetailsQueryHandler::class => InjectionFactory::class, + // Read Video List + ReadVideoListQueryBuilder::class => AutoWiringFactory::class, + ReadVideoListQueryHandler::class => InjectionFactory::class, + ], +]; diff --git a/src/HandlingDomain/Tag/src/Builder/TagAliasBuilder.php b/src/HandlingDomain/Tag/src/Builder/TagAliasBuilder.php new file mode 100644 index 0000000..fc9227a --- /dev/null +++ b/src/HandlingDomain/Tag/src/Builder/TagAliasBuilder.php @@ -0,0 +1,21 @@ +setTag($tag); + $tagAlias->setDescription($description); + + return $tagAlias; + } +} diff --git a/src/HandlingDomain/Tag/src/Builder/TagBuilder.php b/src/HandlingDomain/Tag/src/Builder/TagBuilder.php new file mode 100644 index 0000000..c40a076 --- /dev/null +++ b/src/HandlingDomain/Tag/src/Builder/TagBuilder.php @@ -0,0 +1,18 @@ +setDescription($description); + + return $tag; + } +} diff --git a/src/HandlingDomain/Tag/src/ConfigProvider.php b/src/HandlingDomain/Tag/src/ConfigProvider.php new file mode 100644 index 0000000..7ce7c51 --- /dev/null +++ b/src/HandlingDomain/Tag/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . '/./../config/service_manager.php', + ]; + } +} diff --git a/src/HandlingDomain/Tag/src/Exception/TagAliasAlreadyExistsException.php b/src/HandlingDomain/Tag/src/Exception/TagAliasAlreadyExistsException.php new file mode 100644 index 0000000..82e585c --- /dev/null +++ b/src/HandlingDomain/Tag/src/Exception/TagAliasAlreadyExistsException.php @@ -0,0 +1,25 @@ +toString() + ), + ErrorDomain::Tag, + ErrorCode::NotFound + ); + } +} diff --git a/src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommand.php b/src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommand.php new file mode 100644 index 0000000..fae07e5 --- /dev/null +++ b/src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommand.php @@ -0,0 +1,26 @@ +tagId; + } + + public function getDescription(): string + { + return $this->description; + } +} diff --git a/src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommandBuilder.php b/src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommandBuilder.php new file mode 100644 index 0000000..dd4617e --- /dev/null +++ b/src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommandBuilder.php @@ -0,0 +1,20 @@ +getTagId(); + $description = $addAliasCommand->getDescription(); + + if ($this->existsRule->appliesTo($description)) { + throw new TagAliasAlreadyExistsException($description); + } + + $tag = $this->tagRepository->findOneBy(['id' => $tagId]); + if ($tag === null) { + throw new TagNotFoundByIdException($tagId); + } + + $tagAlias = $this->aliasBuilder->build( + $tag, + $description + ); + + $this->entityManager->persist($tagAlias); + $this->entityManager->flush(); + + return new AddAliasCommandResult($tagAlias); + } +} diff --git a/src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommandResult.php b/src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommandResult.php new file mode 100644 index 0000000..734c2ff --- /dev/null +++ b/src/HandlingDomain/Tag/src/Handler/Command/AddAlias/AddAliasCommandResult.php @@ -0,0 +1,20 @@ +alias; + } +} diff --git a/src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommand.php b/src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommand.php new file mode 100644 index 0000000..0d56fe1 --- /dev/null +++ b/src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommand.php @@ -0,0 +1,18 @@ +description; + } +} diff --git a/src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommandBuilder.php b/src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommandBuilder.php new file mode 100644 index 0000000..72d4acd --- /dev/null +++ b/src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommandBuilder.php @@ -0,0 +1,16 @@ +getDescription(); + + if ($this->existsRule->appliesTo($description)) { + throw new TagAlreadyExistsException($description); + } + + $tag = $this->tagBuilder->build($description); + + $this->entityManager->persist($tag); + $this->entityManager->flush(); + + return new CreateCommandResult($tag); + } +} diff --git a/src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommandResult.php b/src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommandResult.php new file mode 100644 index 0000000..66db0da --- /dev/null +++ b/src/HandlingDomain/Tag/src/Handler/Command/Create/CreateCommandResult.php @@ -0,0 +1,20 @@ +tag; + } +} diff --git a/src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQuery.php b/src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQuery.php new file mode 100644 index 0000000..8cf29e1 --- /dev/null +++ b/src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQuery.php @@ -0,0 +1,20 @@ +tagId; + } +} diff --git a/src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQueryBuilder.php b/src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQueryBuilder.php new file mode 100644 index 0000000..d3ee7ca --- /dev/null +++ b/src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQueryBuilder.php @@ -0,0 +1,18 @@ +getTagId(); + + $tag = $this->tagRepository->findOneBy(['id' => $tagId]); + + if ($tag === null) { + throw new TagNotFoundByIdException($tagId); + } + + return new ReadDetailsQueryResult($tag); + } +} diff --git a/src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQueryResult.php b/src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQueryResult.php new file mode 100644 index 0000000..62b6b07 --- /dev/null +++ b/src/HandlingDomain/Tag/src/Handler/Query/ReadDetails/ReadDetailsQueryResult.php @@ -0,0 +1,20 @@ +tag; + } +} diff --git a/src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQuery.php b/src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQuery.php new file mode 100644 index 0000000..d1e1bd3 --- /dev/null +++ b/src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQuery.php @@ -0,0 +1,38 @@ +tagId; + } + + public function getQuery(): ?string + { + return $this->query; + } + + public function getPage(): int + { + return $this->page; + } + + public function getPerPage(): int + { + return $this->perPage; + } +} diff --git a/src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQueryBuilder.php b/src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQueryBuilder.php new file mode 100644 index 0000000..5bddd32 --- /dev/null +++ b/src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQueryBuilder.php @@ -0,0 +1,24 @@ +getTagId(); + + $tag = $this->tagRepository->findOneBy(['id' => $tagId]); + if ($tag === null) { + throw new TagNotFoundByIdException($tagId); + } + + return new ReadVideoListQueryResult( + $this->videoRepository->findByFilter( + query: $readVideoListQuery->getQuery(), + page: $readVideoListQuery->getPage(), + perPage: $readVideoListQuery->getPerPage(), + onlyTagless: false, + tag: $tag + ) + ); + } +} diff --git a/src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQueryResult.php b/src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQueryResult.php new file mode 100644 index 0000000..6bd75c6 --- /dev/null +++ b/src/HandlingDomain/Tag/src/Handler/Query/ReadVideoList/ReadVideoListQueryResult.php @@ -0,0 +1,20 @@ +paginator; + } +} diff --git a/src/HandlingDomain/Tag/src/Rule/TagAliasExistsRule.php b/src/HandlingDomain/Tag/src/Rule/TagAliasExistsRule.php new file mode 100644 index 0000000..919d241 --- /dev/null +++ b/src/HandlingDomain/Tag/src/Rule/TagAliasExistsRule.php @@ -0,0 +1,27 @@ +tagAliasRepository->findOneBy(['description' => $description]) !== null; + } +} diff --git a/src/HandlingDomain/Tag/src/Rule/TagExistsRule.php b/src/HandlingDomain/Tag/src/Rule/TagExistsRule.php new file mode 100644 index 0000000..19da123 --- /dev/null +++ b/src/HandlingDomain/Tag/src/Rule/TagExistsRule.php @@ -0,0 +1,28 @@ +tagRepository->findOneBy(['description' => $description]) !== null; + } +} diff --git a/src/HandlingDomain/TagList/config/service_manager.php b/src/HandlingDomain/TagList/config/service_manager.php new file mode 100644 index 0000000..7b8cc39 --- /dev/null +++ b/src/HandlingDomain/TagList/config/service_manager.php @@ -0,0 +1,17 @@ + [ + /// CQRS + // ReadList + ReadListQueryBuilder::class => AutoWiringFactory::class, + ReadListQueryHandler::class => InjectionFactory::class, + ], +]; diff --git a/src/HandlingDomain/TagList/src/ConfigProvider.php b/src/HandlingDomain/TagList/src/ConfigProvider.php new file mode 100644 index 0000000..1c922eb --- /dev/null +++ b/src/HandlingDomain/TagList/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . './../config/service_manager.php', + ]; + } +} diff --git a/src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQuery.php b/src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQuery.php new file mode 100644 index 0000000..34b011e --- /dev/null +++ b/src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQuery.php @@ -0,0 +1,18 @@ +query; + } +} diff --git a/src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQueryBuilder.php b/src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQueryBuilder.php new file mode 100644 index 0000000..c56d449 --- /dev/null +++ b/src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQueryBuilder.php @@ -0,0 +1,16 @@ +tagRepository->findByFilter( + $readListQuery->getQuery(), + ) + ); + } +} diff --git a/src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQueryResult.php b/src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQueryResult.php new file mode 100644 index 0000000..5a2d4c8 --- /dev/null +++ b/src/HandlingDomain/TagList/src/Handler/Query/ReadList/ReadListQueryResult.php @@ -0,0 +1,18 @@ +items; + } +} diff --git a/src/HandlingDomain/User/config/service_manager.php b/src/HandlingDomain/User/config/service_manager.php new file mode 100644 index 0000000..d22df72 --- /dev/null +++ b/src/HandlingDomain/User/config/service_manager.php @@ -0,0 +1,39 @@ + [ + + /// Builder + UserBuilder::class => InjectionFactory::class, + + /// Rule + UserWithIdentifierAlreadyExistsRule::class => InjectionFactory::class, + UserPasswordMatchRule::class => AutoWiringFactory::class, + + /// CQRS + // Create User + CreateUserCommandHandler::class => AutoWiringFactory::class, + CreateUserCommandBuilder::class => AutoWiringFactory::class, + + // Change Password + ChangePasswordCommandHandler::class => AutoWiringFactory::class, + ChangePasswordCommandBuilder::class => AutoWiringFactory::class, + + // Change Username + ChangeUsernameCommandHandler::class => AutoWiringFactory::class, + ChangeUsernameCommandBuilder::class => AutoWiringFactory::class, + ], +]; diff --git a/src/HandlingDomain/User/src/Builder/UserBuilder.php b/src/HandlingDomain/User/src/Builder/UserBuilder.php new file mode 100644 index 0000000..7474c7d --- /dev/null +++ b/src/HandlingDomain/User/src/Builder/UserBuilder.php @@ -0,0 +1,53 @@ +roleRepository->findOneBy([ + 'identifier' => $roleIdentifier + ]) ?? null; + + if ($role === null) { + throw new RoleNotFoundByIdentifierException($roleIdentifier); + } + + $encryptedPassword = $this->encryptionClient->encrypt($password); + + $user = new User(); + $user->setRole($role); + $user->setUsername($username); + $user->setMail($mail); + $user->setPassword($encryptedPassword); + + return $user; + } +} diff --git a/src/HandlingDomain/User/src/ConfigProvider.php b/src/HandlingDomain/User/src/ConfigProvider.php new file mode 100644 index 0000000..25236b9 --- /dev/null +++ b/src/HandlingDomain/User/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . '/./../config/service_manager.php', + ]; + } +} diff --git a/src/HandlingDomain/User/src/Exception/UserNotFoundByIdentifierException.php b/src/HandlingDomain/User/src/Exception/UserNotFoundByIdentifierException.php new file mode 100644 index 0000000..69c64a1 --- /dev/null +++ b/src/HandlingDomain/User/src/Exception/UserNotFoundByIdentifierException.php @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/src/HandlingDomain/User/src/Exception/UserPasswordMismatchException.php b/src/HandlingDomain/User/src/Exception/UserPasswordMismatchException.php new file mode 100644 index 0000000..1a91a21 --- /dev/null +++ b/src/HandlingDomain/User/src/Exception/UserPasswordMismatchException.php @@ -0,0 +1,21 @@ +user; + } + + public function getNewPassword(): string { + return $this->newPassword; + } + + public function getPassword(): string { + return $this->password; + } +} diff --git a/src/HandlingDomain/User/src/Handler/Command/ChangePassword/ChangePasswordCommandBuilder.php b/src/HandlingDomain/User/src/Handler/Command/ChangePassword/ChangePasswordCommandBuilder.php new file mode 100644 index 0000000..36428f8 --- /dev/null +++ b/src/HandlingDomain/User/src/Handler/Command/ChangePassword/ChangePasswordCommandBuilder.php @@ -0,0 +1,21 @@ +getUser(); + + if (!$this->encryptionClient->verify($command->getPassword(), $user->getPassword())) { + throw new UserWrongPasswordException(); + } + + $encryptedPassword = $this->encryptionClient->encrypt( + $command->getNewPassword() + ); + $user->setPassword($encryptedPassword); + + $this->entityManager->persist($user); + $this->entityManager->flush(); + } +} diff --git a/src/HandlingDomain/User/src/Handler/Command/ChangeUsername/ChangeUsernameCommand.php b/src/HandlingDomain/User/src/Handler/Command/ChangeUsername/ChangeUsernameCommand.php new file mode 100644 index 0000000..1b97c76 --- /dev/null +++ b/src/HandlingDomain/User/src/Handler/Command/ChangeUsername/ChangeUsernameCommand.php @@ -0,0 +1,28 @@ +user; + } + + public function getNewUsername(): string { + return $this->newUsername; + } + + public function getPassword(): string { + return $this->password; + } +} + diff --git a/src/HandlingDomain/User/src/Handler/Command/ChangeUsername/ChangeUsernameCommandBuilder.php b/src/HandlingDomain/User/src/Handler/Command/ChangeUsername/ChangeUsernameCommandBuilder.php new file mode 100644 index 0000000..c50edb4 --- /dev/null +++ b/src/HandlingDomain/User/src/Handler/Command/ChangeUsername/ChangeUsernameCommandBuilder.php @@ -0,0 +1,21 @@ +getUser(); + + if (!$this->encryptionClient->verify($command->getPassword(), $user->getPassword())) { + throw new UserWrongPasswordException(); + } + + $user->setUsername($command->getNewUsername()); + + $this->entityManager->persist($user); + $this->entityManager->flush(); + } +} diff --git a/src/HandlingDomain/User/src/Handler/Command/CreateUser/CreateUserCommand.php b/src/HandlingDomain/User/src/Handler/Command/CreateUser/CreateUserCommand.php new file mode 100644 index 0000000..1d79920 --- /dev/null +++ b/src/HandlingDomain/User/src/Handler/Command/CreateUser/CreateUserCommand.php @@ -0,0 +1,25 @@ +username; + } + + public function getMail(): string { + return $this->mail; + } + + public function getPassword(): string { + return $this->password; + } +} \ No newline at end of file diff --git a/src/HandlingDomain/User/src/Handler/Command/CreateUser/CreateUserCommandBuilder.php b/src/HandlingDomain/User/src/Handler/Command/CreateUser/CreateUserCommandBuilder.php new file mode 100644 index 0000000..970d2b4 --- /dev/null +++ b/src/HandlingDomain/User/src/Handler/Command/CreateUser/CreateUserCommandBuilder.php @@ -0,0 +1,19 @@ +userWithIdentifierAlreadyExistsRule->appliesTo($command->getUsername()); + $this->userWithIdentifierAlreadyExistsRule->appliesTo($command->getMail()); + + $user = $this->builder->build( + username: $command->getUsername(), + roleIdentifier: 'user', + mail: $command->getMail(), + password: $command->getPassword() + ); + + $this->entityManager->persist($user); + $this->entityManager->flush(); + + return $user; + } +} + +?> \ No newline at end of file diff --git a/src/HandlingDomain/User/src/Rule/UserPasswordMatchRule.php b/src/HandlingDomain/User/src/Rule/UserPasswordMatchRule.php new file mode 100644 index 0000000..b49037d --- /dev/null +++ b/src/HandlingDomain/User/src/Rule/UserPasswordMatchRule.php @@ -0,0 +1,15 @@ +userRepository->findByIdentifier($identifier); + + if ($user !== null) { + throw new UserWithIdentifierAlreadyExists($identifier); + } + } +} \ No newline at end of file diff --git a/src/HandlingDomain/UserSession/config/service_manager.php b/src/HandlingDomain/UserSession/config/service_manager.php new file mode 100644 index 0000000..fb94f93 --- /dev/null +++ b/src/HandlingDomain/UserSession/config/service_manager.php @@ -0,0 +1,31 @@ + [ + + /// Rule + UserPasswordMatchesRule::class => AutoWiringFactory::class, + + /// Builder + UserSessionBuilder::class => AutoWiringFactory::class, + + /// CQRS + // Login User + LoginUserCommandHandler::class => InjectionFactory::class, + LoginUserCommandBuilder::class => AutoWiringFactory::class, + + // Logout User + LogoutUserCommandHandler::class => InjectionFactory::class, + LogoutUserCommandBuilder::class => AutoWiringFactory::class, + ], +]; diff --git a/src/HandlingDomain/UserSession/src/Builder/UserSessionBuilder.php b/src/HandlingDomain/UserSession/src/Builder/UserSessionBuilder.php new file mode 100644 index 0000000..8b382f7 --- /dev/null +++ b/src/HandlingDomain/UserSession/src/Builder/UserSessionBuilder.php @@ -0,0 +1,17 @@ +setUser(null); + $userSession->setUserId(null); + $userSession->setCsrf(null); + return $userSession; + } +} diff --git a/src/HandlingDomain/UserSession/src/ConfigProvider.php b/src/HandlingDomain/UserSession/src/ConfigProvider.php new file mode 100644 index 0000000..92ee2eb --- /dev/null +++ b/src/HandlingDomain/UserSession/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . '/./../config/service_manager.php', + ]; + } +} diff --git a/src/HandlingDomain/UserSession/src/Handler/Command/LoginUser/LoginUserCommand.php b/src/HandlingDomain/UserSession/src/Handler/Command/LoginUser/LoginUserCommand.php new file mode 100644 index 0000000..fa79d64 --- /dev/null +++ b/src/HandlingDomain/UserSession/src/Handler/Command/LoginUser/LoginUserCommand.php @@ -0,0 +1,29 @@ +session; + } + + public function getIdentifier(): string { + return $this->identifier; + } + + public function getPassword(): string { + return $this->password; + } +} + +?> \ No newline at end of file diff --git a/src/HandlingDomain/UserSession/src/Handler/Command/LoginUser/LoginUserCommandBuilder.php b/src/HandlingDomain/UserSession/src/Handler/Command/LoginUser/LoginUserCommandBuilder.php new file mode 100644 index 0000000..5512287 --- /dev/null +++ b/src/HandlingDomain/UserSession/src/Handler/Command/LoginUser/LoginUserCommandBuilder.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/src/HandlingDomain/UserSession/src/Handler/Command/LoginUser/LoginUserCommandHandler.php b/src/HandlingDomain/UserSession/src/Handler/Command/LoginUser/LoginUserCommandHandler.php new file mode 100644 index 0000000..23ce0ec --- /dev/null +++ b/src/HandlingDomain/UserSession/src/Handler/Command/LoginUser/LoginUserCommandHandler.php @@ -0,0 +1,80 @@ +userRepository->findByIdentifier( + $command->getIdentifier() + ); + + if ($user === null) { + throw new UserNotFoundByIdentifierException($command->getIdentifier()); + } + + if ( + !$this->passwordMatchesRule->appliesTo( + $command->getPassword(), + $user + ) + ) { + throw new UserWrongPasswordException(); + } + + $oldUserSessions = $this->userSessionRepository->findBy([ + 'userId' => $user->getId() + ]); + + foreach ($oldUserSessions as $oldUserSession) { + $this->entityManager->remove($oldUserSession); + } + + $session = $command->getSession(); + $session->setUser($user); + $user->setLastLoginAt(new DateTime('now')); + + $this->entityManager->persist($session); + $this->entityManager->flush(); + + return $session; + } +} + +?> \ No newline at end of file diff --git a/src/HandlingDomain/UserSession/src/Handler/Command/LogoutUser/LogoutUserCommand.php b/src/HandlingDomain/UserSession/src/Handler/Command/LogoutUser/LogoutUserCommand.php new file mode 100644 index 0000000..a4eaa29 --- /dev/null +++ b/src/HandlingDomain/UserSession/src/Handler/Command/LogoutUser/LogoutUserCommand.php @@ -0,0 +1,19 @@ +session; + } +} + +?> \ No newline at end of file diff --git a/src/HandlingDomain/UserSession/src/Handler/Command/LogoutUser/LogoutUserCommandBuilder.php b/src/HandlingDomain/UserSession/src/Handler/Command/LogoutUser/LogoutUserCommandBuilder.php new file mode 100644 index 0000000..f6fa886 --- /dev/null +++ b/src/HandlingDomain/UserSession/src/Handler/Command/LogoutUser/LogoutUserCommandBuilder.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/src/HandlingDomain/UserSession/src/Handler/Command/LogoutUser/LogoutUserCommandHandler.php b/src/HandlingDomain/UserSession/src/Handler/Command/LogoutUser/LogoutUserCommandHandler.php new file mode 100644 index 0000000..9c8bf4f --- /dev/null +++ b/src/HandlingDomain/UserSession/src/Handler/Command/LogoutUser/LogoutUserCommandHandler.php @@ -0,0 +1,37 @@ +getSession(); + $this->entityManager->remove($session); + $this->entityManager->flush(); + + return $session; + } +} + +?> \ No newline at end of file diff --git a/src/HandlingDomain/UserSession/src/Rule/UserPasswordMatchesRule.php b/src/HandlingDomain/UserSession/src/Rule/UserPasswordMatchesRule.php new file mode 100644 index 0000000..6a5f17b --- /dev/null +++ b/src/HandlingDomain/UserSession/src/Rule/UserPasswordMatchesRule.php @@ -0,0 +1,24 @@ +encryptionClient->verify( + $password, + $user->getPassword() + ); + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Video/config/service_manager.php b/src/HandlingDomain/Video/config/service_manager.php new file mode 100644 index 0000000..ae3cf98 --- /dev/null +++ b/src/HandlingDomain/Video/config/service_manager.php @@ -0,0 +1,94 @@ + [ + /// Analyzer + VideoTitleAnalyzer::class => InjectionFactory::class, + VideoDurationAnalyzer::class => AutoWiringFactory::class, + + /// Uploader + VideoUploader::class => AutoWiringFactory::class, + + /// Builder + VideoBuilder::class => AutoWiringFactory::class, + VideoImageBuilder::class => AutoWiringFactory::class, + VideoEventBuilder::class => AutoWiringFactory::class, + + /// Rule + VideoExistsRule::class => InjectionFactory::class, + + /// CQRS + // Stream Query + StreamQueryHandler::class => AutoWiringFactory::class, + StreamQueryBuilder::class => AutoWiringFactory::class, + // Thumbnail Query + ThumbnailQueryHandler::class => InjectionFactory::class, + ThumbnailQueryBuilder::class => AutoWiringFactory::class, + // Upload Command + UploadCommandHandler::class => AutoWiringFactory::class, + UploadCommandBuilder::class => AutoWiringFactory::class, + // Read Details + ReadDetailsQueryHandler::class => InjectionFactory::class, + ReadDetailsQueryBuilder::class => AutoWiringFactory::class, + // Set Thumbnail + SetThumbnailCommandHandler::class => InjectionFactory::class, + SetThumbnailCommandBuilder::class => AutoWiringFactory::class, + // Update Details + UpdateDetailsCommandHandler::class => InjectionFactory::class, + UpdateDetailsCommandBuilder::class => AutoWiringFactory::class, + // Delete + DeleteCommandHandler::class => InjectionFactory::class, + DeleteCommandBuilder::class => AutoWiringFactory::class, + + /// Pipeline + // Upload + UploadPipeline::class => AutoWiringFactory::class, + CheckVideoStep::class => AutoWiringFactory::class, + BuildVideoStep::class => AutoWiringFactory::class, + AnalyzeTitleStep::class => AutoWiringFactory::class, + AnalyzeDurationStep::class => AutoWiringFactory::class, + UploadFileStep::class => AutoWiringFactory::class, + UploadSaveEntitiesStep::class => AutoWiringFactory::class, + // Stream + StreamPipeline::class => AutoWiringFactory::class, + LoadVideoStep::class => InjectionFactory::class, + CreateEventStep::class => InjectionFactory::class, + StreamSaveEntitiesStep::class => AutoWiringFactory::class, + ], +]; diff --git a/src/HandlingDomain/Video/src/Analyzer/VideoDurationAnalyzer.php b/src/HandlingDomain/Video/src/Analyzer/VideoDurationAnalyzer.php new file mode 100644 index 0000000..da1693b --- /dev/null +++ b/src/HandlingDomain/Video/src/Analyzer/VideoDurationAnalyzer.php @@ -0,0 +1,32 @@ +getDuration() === null) { + $command = sprintf( + "ffprobe -i %s%s -show_entries format=duration -v quiet -of csv='p=0'", + $video->getDirectoryPath(), + "video.mp4" + ); + + $duration = shell_exec($command); + if ($duration !== null) { + $video->setDuration(intval($duration)); + } + } + } +} diff --git a/src/HandlingDomain/Video/src/Analyzer/VideoTitleAnalyzer.php b/src/HandlingDomain/Video/src/Analyzer/VideoTitleAnalyzer.php new file mode 100644 index 0000000..71b0474 --- /dev/null +++ b/src/HandlingDomain/Video/src/Analyzer/VideoTitleAnalyzer.php @@ -0,0 +1,66 @@ +normalizeString($video->getTitle()); + + $tags = $this->tagRepository->findAll(); + $matches = []; + + /** @var Tag $tag */ + foreach ($tags as $tag) { + if (str_contains($title, $this->normalizeString($tag->getDescription()))) { + $matches[] = $tag; + continue; + } + + /** @var TagAlias $alias */ + foreach ($tag->getAliases() as $alias) { + if (str_contains($title, $this->normalizeString($alias->getDescription()))) { + $matches[] = $tag; + break; + } + } + } + + /** @var Tag $match */ + foreach ($matches as $match) { + $video->addTag($match); + } + + return $matches; + } + + private function normalizeString(string $string): string { + return preg_replace( + '/[^a-zA-Z0-9]+/', + '', + strtolower($string) + ); + } +} diff --git a/src/HandlingDomain/Video/src/Builder/VideoBuilder.php b/src/HandlingDomain/Video/src/Builder/VideoBuilder.php new file mode 100644 index 0000000..8b029fd --- /dev/null +++ b/src/HandlingDomain/Video/src/Builder/VideoBuilder.php @@ -0,0 +1,21 @@ +setTitle($title); + $video->setDirectoryPath($directoryPath); + $video->setDuration(null); + + return $video; + } +} diff --git a/src/HandlingDomain/Video/src/Builder/VideoEventBuilder.php b/src/HandlingDomain/Video/src/Builder/VideoEventBuilder.php new file mode 100644 index 0000000..2e1cb05 --- /dev/null +++ b/src/HandlingDomain/Video/src/Builder/VideoEventBuilder.php @@ -0,0 +1,29 @@ +setVideo($video); + $videoEvent->setType($eventType->value); + + if ($dateTime != null) { + $videoEvent->setDateTime($dateTime); + } + + return $videoEvent; + } +} diff --git a/src/HandlingDomain/Video/src/Builder/VideoImageBuilder.php b/src/HandlingDomain/Video/src/Builder/VideoImageBuilder.php new file mode 100644 index 0000000..dfbef63 --- /dev/null +++ b/src/HandlingDomain/Video/src/Builder/VideoImageBuilder.php @@ -0,0 +1,55 @@ +toString() + ); + + $targetPath = sprintf( + '%s/%s', + $directoryPath, + self::FILE_NAME + ); + + if (file_exists($targetPath)) { + if (!$override) { + return null; + } + + unlink($targetPath); + } + + $command = sprintf( + "cd %s && ffmpeg -i video.mp4 -ss %s -vframes 1 %s", + $directoryPath, + $timestamp, + self::FILE_NAME + ); + + shell_exec($command); + + return $targetPath; + } +} diff --git a/src/HandlingDomain/Video/src/ConfigProvider.php b/src/HandlingDomain/Video/src/ConfigProvider.php new file mode 100644 index 0000000..067bcff --- /dev/null +++ b/src/HandlingDomain/Video/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . '/./../config/service_manager.php', + ]; + } +} diff --git a/src/HandlingDomain/Video/src/Enum/VideoEventType.php b/src/HandlingDomain/Video/src/Enum/VideoEventType.php new file mode 100644 index 0000000..6fb8898 --- /dev/null +++ b/src/HandlingDomain/Video/src/Enum/VideoEventType.php @@ -0,0 +1,8 @@ +getClientFilename() + ), + ErrorDomain::Video, + ErrorCode::AlreadyExists + ); + } +} \ No newline at end of file diff --git a/src/HandlingDomain/Video/src/Exception/VideoNotFoundByIdException.php b/src/HandlingDomain/Video/src/Exception/VideoNotFoundByIdException.php new file mode 100644 index 0000000..412eb47 --- /dev/null +++ b/src/HandlingDomain/Video/src/Exception/VideoNotFoundByIdException.php @@ -0,0 +1,25 @@ +toString() + ), + ErrorDomain::Video, + ErrorCode::NotFound + ); + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommand.php b/src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommand.php new file mode 100644 index 0000000..25a7266 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommand.php @@ -0,0 +1,20 @@ +videoId; + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommandBuilder.php b/src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommandBuilder.php new file mode 100644 index 0000000..d831176 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommandBuilder.php @@ -0,0 +1,18 @@ +repository->findOneBy(['id' => $deleteCommand->getVideoId()]); + + if ($video === null) { + throw new VideoNotFoundByIdException($deleteCommand->getVideoId()); + } + + $directory = $video->getDirectoryPath(); + $thumbnailFile = $directory . 'thumbnail.png'; + $videoFile = $directory . 'video.mp4'; + + if (file_exists($thumbnailFile) && !unlink($thumbnailFile)) { + throw new MyTubeException('Could not delete thumbnail'); + } + if (file_exists($videoFile) && !unlink($videoFile)) { + throw new MyTubeException('Could not delete video'); + } + + $this->entityManager->remove($video); + $this->entityManager->flush(); + + if (is_dir($directory) && !rmdir($directory)) { + throw new MyTubeException('Could not delete directory'); + } + + return new DeleteCommandResult(); + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommandResult.php b/src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommandResult.php new file mode 100644 index 0000000..d2aa59a --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Command/Delete/DeleteCommandResult.php @@ -0,0 +1,13 @@ +videoId; + } + + public function getSeconds(): float + { + return $this->seconds; + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Command/SetThumbnail/SetThumbnailCommandBuilder.php b/src/HandlingDomain/Video/src/Handler/Command/SetThumbnail/SetThumbnailCommandBuilder.php new file mode 100644 index 0000000..01b188f --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Command/SetThumbnail/SetThumbnailCommandBuilder.php @@ -0,0 +1,20 @@ +getVideoId(); + $seconds = $setThumbnailCommand->getSeconds(); + + $video = $this->videoRepository->findOneBy(['id' => $videoId]); + if ($video === null) { + throw new VideoNotFoundByIdException($videoId); + } + + $this->imageBuilder->build( + $videoId, + $this->floatToTimestamp($seconds), + true + ); + + return new SetThumbnailCommandResult(); + } + + private function floatToTimestamp(float $seconds) { + $hours = floor((int)$seconds / 3600); + $minutes = floor(((int)$seconds % 3600) / 60); + $seconds = (int)$seconds % 60; + + return sprintf("%02d:%02d:%02d", $hours, $minutes, $seconds); + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Command/SetThumbnail/SetThumbnailCommandResult.php b/src/HandlingDomain/Video/src/Handler/Command/SetThumbnail/SetThumbnailCommandResult.php new file mode 100644 index 0000000..8036036 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Command/SetThumbnail/SetThumbnailCommandResult.php @@ -0,0 +1,13 @@ +videoId; + } + + public function getTitle(): ?string + { + return $this->title; + } + + public function getTagIds(): ?array + { + return $this->tagIds; + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Command/UpdateDetails/UpdateDetailsCommandBuilder.php b/src/HandlingDomain/Video/src/Handler/Command/UpdateDetails/UpdateDetailsCommandBuilder.php new file mode 100644 index 0000000..1c93200 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Command/UpdateDetails/UpdateDetailsCommandBuilder.php @@ -0,0 +1,22 @@ +repository->findOneBy(['id' => $updateDetailsCommand->getVideoId()]); + + if ($video === null) { + throw new VideoNotFoundByIdException($updateDetailsCommand->getVideoId()); + } + + $this->durationAnalyzer->analyze($video); + + if ($updateDetailsCommand->getTitle() !== null) { + $video->setTitle($updateDetailsCommand->getTitle()); + } + + if ($updateDetailsCommand->getTitle() !== null) { + $video->setTitle($updateDetailsCommand->getTitle()); + } + + if ($updateDetailsCommand->getTagIds() !== null) { + $tags = []; + foreach ($updateDetailsCommand->getTagIds() as $id) { + $tags[] = $this->tagRepository->findOneBy(['id' => $id]); + } + foreach ($video->getTags() as $tag) { + $video->removeTag($tag); + } + foreach ($tags as $tag) { + $video->addTag($tag); + } + } + + $this->entityManager->persist($video); + $this->entityManager->flush(); + + return new UpdateDetailsCommandResult(); + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Command/UpdateDetails/UpdateDetailsCommandResult.php b/src/HandlingDomain/Video/src/Handler/Command/UpdateDetails/UpdateDetailsCommandResult.php new file mode 100644 index 0000000..49f7b87 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Command/UpdateDetails/UpdateDetailsCommandResult.php @@ -0,0 +1,13 @@ +uploadedFile; + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Command/Upload/UploadCommandBuilder.php b/src/HandlingDomain/Video/src/Handler/Command/Upload/UploadCommandBuilder.php new file mode 100644 index 0000000..5615cf5 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Command/Upload/UploadCommandBuilder.php @@ -0,0 +1,18 @@ +setUploadedFile($uploadCommand->getUploadedFile()); + + $this->pipeline->handle($payload); + + return $payload->getVideo(); + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Query/Delete/DeleteQuery.php b/src/HandlingDomain/Video/src/Handler/Query/Delete/DeleteQuery.php new file mode 100644 index 0000000..ae3dfe8 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Query/Delete/DeleteQuery.php @@ -0,0 +1,13 @@ +videoId; + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Query/ReadDetails/ReadDetailsQueryBuilder.php b/src/HandlingDomain/Video/src/Handler/Query/ReadDetails/ReadDetailsQueryBuilder.php new file mode 100644 index 0000000..96b9f49 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Query/ReadDetails/ReadDetailsQueryBuilder.php @@ -0,0 +1,18 @@ +getVideoId(); + + /** @var Video $video */ + $video = $this->videoRepository->findOneBy(['id' => $videoId]); + + if ($video === null) { + throw new VideoNotFoundByIdException($videoId); + } + + $this->videoImageBuilder->build($video->getId()); + + return new ReadDetailsQueryResult($video); + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Query/ReadDetails/ReadDetailsQueryResult.php b/src/HandlingDomain/Video/src/Handler/Query/ReadDetails/ReadDetailsQueryResult.php new file mode 100644 index 0000000..e060b3c --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Query/ReadDetails/ReadDetailsQueryResult.php @@ -0,0 +1,20 @@ +video; + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Query/Stream/StreamQuery.php b/src/HandlingDomain/Video/src/Handler/Query/Stream/StreamQuery.php new file mode 100644 index 0000000..b9682f5 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Query/Stream/StreamQuery.php @@ -0,0 +1,20 @@ +videoUuid; + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Query/Stream/StreamQueryBuilder.php b/src/HandlingDomain/Video/src/Handler/Query/Stream/StreamQueryBuilder.php new file mode 100644 index 0000000..460cd51 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Query/Stream/StreamQueryBuilder.php @@ -0,0 +1,18 @@ +setVideoUuid($streamQuery->getVideoUuid()); + + $this->pipeline->handle($payload); + + return $payload->getVideo(); + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQuery.php b/src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQuery.php new file mode 100644 index 0000000..0806e40 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQuery.php @@ -0,0 +1,20 @@ +videoUuid; + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQueryBuilder.php b/src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQueryBuilder.php new file mode 100644 index 0000000..30d1174 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQueryBuilder.php @@ -0,0 +1,18 @@ +videoRepository->findOneBy([ + 'id' => $streamQuery->getVideoUuid() + ]); + + if ($video === null) { + throw new Exception('Video not found'); + } + + return new ThumbnailQueryResult($video); + } +} diff --git a/src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQueryResult.php b/src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQueryResult.php new file mode 100644 index 0000000..e0e45f7 --- /dev/null +++ b/src/HandlingDomain/Video/src/Handler/Query/Thumbnail/ThumbnailQueryResult.php @@ -0,0 +1,20 @@ +video; + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Stream/Step/CreateEventStep.php b/src/HandlingDomain/Video/src/Pipeline/Stream/Step/CreateEventStep.php new file mode 100644 index 0000000..f2f3998 --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Stream/Step/CreateEventStep.php @@ -0,0 +1,59 @@ +getVideo(); + + $videoWatchedEvent = $this->videoEventRepository->findOneBy([ + 'video' => $video, + 'type' => VideoEventType::Watched->value, + 'dateTime' => $now + ]); + + if ($videoWatchedEvent === null) { + $watchedEvent = $this->videoEventBuilder->build( + $video, + VideoEventType::Watched, + $now + ); + + $streamPayload->setEvent($watchedEvent); + } + + $pipeline->next()($payload, $pipeline); + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Stream/Step/LoadVideoStep.php b/src/HandlingDomain/Video/src/Pipeline/Stream/Step/LoadVideoStep.php new file mode 100644 index 0000000..61d0e84 --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Stream/Step/LoadVideoStep.php @@ -0,0 +1,50 @@ +getVideoUuid(); + + $video = $this->videoRepository->findOneBy([ + 'id' => $videoId + ]); + + if ($video === null) { + throw new VideoNotFoundByIdException($videoId); + } + + $streamPayload->setVideo($video); + + $pipeline->next()($payload, $pipeline); + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Stream/Step/SaveEntitiesStep.php b/src/HandlingDomain/Video/src/Pipeline/Stream/Step/SaveEntitiesStep.php new file mode 100644 index 0000000..985c2c3 --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Stream/Step/SaveEntitiesStep.php @@ -0,0 +1,33 @@ +getEvent(); + + if ($watchedEvent !== null) { + $this->entityManager->persist($watchedEvent); + } + + $this->entityManager->flush(); + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Stream/StreamPayload.php b/src/HandlingDomain/Video/src/Pipeline/Stream/StreamPayload.php new file mode 100644 index 0000000..b62bf62 --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Stream/StreamPayload.php @@ -0,0 +1,43 @@ +videoUuid; + } + public function setVideoUuid(UuidInterface $videoUuid): void + { + $this->videoUuid = $videoUuid; + } + + public function getVideo(): Video + { + return $this->video; + } + public function setVideo(Video $video): void + { + $this->video = $video; + } + + public function getEvent(): ?VideoEvent + { + return $this->event; + } + public function setEvent(?VideoEvent $event): void + { + $this->event = $event; + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Stream/StreamPipeline.php b/src/HandlingDomain/Video/src/Pipeline/Stream/StreamPipeline.php new file mode 100644 index 0000000..2c039be --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Stream/StreamPipeline.php @@ -0,0 +1,25 @@ +loadVideoStep, + $this->createEventStep, + $this->saveEntitiesStep, + ]); + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Upload/Step/AnalyzeDurationStep.php b/src/HandlingDomain/Video/src/Pipeline/Upload/Step/AnalyzeDurationStep.php new file mode 100644 index 0000000..ad9411d --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Upload/Step/AnalyzeDurationStep.php @@ -0,0 +1,33 @@ +getVideo(); + + $this->analyzer->analyze($video); + + $pipeline->next()($payload, $pipeline); + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Upload/Step/AnalyzeTitleStep.php b/src/HandlingDomain/Video/src/Pipeline/Upload/Step/AnalyzeTitleStep.php new file mode 100644 index 0000000..fa27d6b --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Upload/Step/AnalyzeTitleStep.php @@ -0,0 +1,33 @@ +getVideo(); + + $this->analyzer->analyze($video); + + $pipeline->next()($payload, $pipeline); + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Upload/Step/BuildVideoStep.php b/src/HandlingDomain/Video/src/Pipeline/Upload/Step/BuildVideoStep.php new file mode 100644 index 0000000..650c629 --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Upload/Step/BuildVideoStep.php @@ -0,0 +1,36 @@ +getUploadedFile(); + + $video = $this->videoBuilder->build( + $uploadedFile->getClientFilename() + ); + + $uploadPayload->setVideo($video); + $pipeline->next()($payload, $pipeline); + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Upload/Step/CheckVideoStep.php b/src/HandlingDomain/Video/src/Pipeline/Upload/Step/CheckVideoStep.php new file mode 100644 index 0000000..86352d2 --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Upload/Step/CheckVideoStep.php @@ -0,0 +1,40 @@ +getUploadedFile(); + + if ($this->existsRule->appliesTo($uploadedFile)) { + throw new VideoAlreadyExistsException($uploadedFile); + } + + $pipeline->next()($payload, $pipeline); + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Upload/Step/SaveEntitiesStep.php b/src/HandlingDomain/Video/src/Pipeline/Upload/Step/SaveEntitiesStep.php new file mode 100644 index 0000000..a410005 --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Upload/Step/SaveEntitiesStep.php @@ -0,0 +1,31 @@ +getVideo(); + + $this->entityManager->persist($video); + $this->entityManager->flush(); + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Upload/Step/UploadFileStep.php b/src/HandlingDomain/Video/src/Pipeline/Upload/Step/UploadFileStep.php new file mode 100644 index 0000000..cbcfdd3 --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Upload/Step/UploadFileStep.php @@ -0,0 +1,42 @@ +getVideo(); + $uploadedFile = $uploadPayload->getUploadedFile(); + + $directoryPath = $this->uploader->upload($uploadedFile, $video->getId()); + $video->setDirectoryPath($directoryPath); + + $this->imageBuilder->build($video->getId()); + + $pipeline->next()($payload, $pipeline); + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Upload/UploadPayload.php b/src/HandlingDomain/Video/src/Pipeline/Upload/UploadPayload.php new file mode 100644 index 0000000..c0e86e7 --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Upload/UploadPayload.php @@ -0,0 +1,32 @@ +uploadedFile; + } + public function setUploadedFile(UploadedFileInterface $uploadedFile): void + { + $this->uploadedFile = $uploadedFile; + } + + public function getVideo(): Video + { + return $this->video; + } + public function setVideo(Video $video): void + { + $this->video = $video; + } +} diff --git a/src/HandlingDomain/Video/src/Pipeline/Upload/UploadPipeline.php b/src/HandlingDomain/Video/src/Pipeline/Upload/UploadPipeline.php new file mode 100644 index 0000000..85a7790 --- /dev/null +++ b/src/HandlingDomain/Video/src/Pipeline/Upload/UploadPipeline.php @@ -0,0 +1,46 @@ +checkVideoStep, + $this->buildVideoStep, + $this->analyzeTitleStep, + $this->uploadFileStep, + $this->analyzeDurationStep, + $this->saveEntityStep, + ]); + } + + public function reset(): void + { + $this->tasks = [ + $this->checkVideoStep, + $this->buildVideoStep, + $this->analyzeTitleStep, + $this->uploadFileStep, + $this->analyzeDurationStep, + $this->saveEntityStep, + ]; + } +} diff --git a/src/HandlingDomain/Video/src/Rule/VideoExistsRule.php b/src/HandlingDomain/Video/src/Rule/VideoExistsRule.php new file mode 100644 index 0000000..e4d8874 --- /dev/null +++ b/src/HandlingDomain/Video/src/Rule/VideoExistsRule.php @@ -0,0 +1,29 @@ +tagRepository->findOneBy(['title' => $uploadedFile->getClientFilename()]) !== null; + } +} diff --git a/src/HandlingDomain/Video/src/Uploader/VideoUploader.php b/src/HandlingDomain/Video/src/Uploader/VideoUploader.php new file mode 100644 index 0000000..4829fdf --- /dev/null +++ b/src/HandlingDomain/Video/src/Uploader/VideoUploader.php @@ -0,0 +1,34 @@ +toString(), + ); + + if (file_exists($directoryPath)) { + throw new Exception('File already exists'); + } + + mkdir($directoryPath, 0777, true); + + $targetPath = $directoryPath . 'video.' . substr(strrchr($file->getClientFilename(),'.'),1); + $file->moveTo($targetPath); + + return $directoryPath; + } +} diff --git a/src/HandlingDomain/VideoList/config/service_manager.php b/src/HandlingDomain/VideoList/config/service_manager.php new file mode 100644 index 0000000..7236630 --- /dev/null +++ b/src/HandlingDomain/VideoList/config/service_manager.php @@ -0,0 +1,21 @@ + [ + /// CQRS + // Read List + ReadListQueryBuilder::class => AutoWiringFactory::class, + ReadListQueryHandler::class => InjectionFactory::class, + // Upload + UploadCommandBuilder::class => AutoWiringFactory::class, + UploadCommandHandler::class => AutoWiringFactory::class, + ], +]; diff --git a/src/HandlingDomain/VideoList/src/ConfigProvider.php b/src/HandlingDomain/VideoList/src/ConfigProvider.php new file mode 100644 index 0000000..5f92887 --- /dev/null +++ b/src/HandlingDomain/VideoList/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . '/./../config/service_manager.php', + ]; + } +} diff --git a/src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommand.php b/src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommand.php new file mode 100644 index 0000000..444e9e3 --- /dev/null +++ b/src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommand.php @@ -0,0 +1,18 @@ +uploadedFiles; + } +} diff --git a/src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommandBuilder.php b/src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommandBuilder.php new file mode 100644 index 0000000..de072c3 --- /dev/null +++ b/src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommandBuilder.php @@ -0,0 +1,16 @@ +getUploadedFiles() as $uploadedFile) { + try { + $payload = new UploadPayload(); + $payload->setUploadedFile($uploadedFile); + + $this->pipeline->reset(); + $this->pipeline->handle($payload); + + $uploadedFiles[] = UploadedFileResult::fromVideo($uploadedFile, $payload->getVideo()); + } catch (Throwable $e) { + $uploadedFiles[] = UploadedFileResult::fromException($uploadedFile, $e); + $this->logger->exception($e); + $this->logger->info($e->getTraceAsString()); + } + } + + return new UploadCommandResult($uploadedFiles); + } +} diff --git a/src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommandResult.php b/src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommandResult.php new file mode 100644 index 0000000..e0c5b66 --- /dev/null +++ b/src/HandlingDomain/VideoList/src/Handler/Command/Upload/UploadCommandResult.php @@ -0,0 +1,21 @@ +uploadedFileResults; + } +} diff --git a/src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQuery.php b/src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQuery.php new file mode 100644 index 0000000..b0f10c2 --- /dev/null +++ b/src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQuery.php @@ -0,0 +1,36 @@ +query; + } + + public function getPage(): int + { + return $this->page; + } + + public function getPerPage(): int + { + return $this->perPage; + } + + public function getOnlyTagless(): bool + { + return $this->onlyTagless; + } +} diff --git a/src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQueryBuilder.php b/src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQueryBuilder.php new file mode 100644 index 0000000..42fbb84 --- /dev/null +++ b/src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQueryBuilder.php @@ -0,0 +1,22 @@ +videoRepository->findByFilter( + query: $readListQuery->getQuery(), + page: $readListQuery->getPage(), + perPage: $readListQuery->getPerPage(), + onlyTagless: $readListQuery->getOnlyTagless() + ) + ); + } +} diff --git a/src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQueryResult.php b/src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQueryResult.php new file mode 100644 index 0000000..cb88e9e --- /dev/null +++ b/src/HandlingDomain/VideoList/src/Handler/Query/ReadList/ReadListQueryResult.php @@ -0,0 +1,20 @@ +paginator; + } +} diff --git a/src/HandlingDomain/VideoList/src/Model/UploadedFileResult.php b/src/HandlingDomain/VideoList/src/Model/UploadedFileResult.php new file mode 100644 index 0000000..77a95f3 --- /dev/null +++ b/src/HandlingDomain/VideoList/src/Model/UploadedFileResult.php @@ -0,0 +1,66 @@ +success = $this->video != null; + } + + public function getUploadedFile(): UploadedFileInterface + { + return $this->uploadedFile; + } + + public function getSuccess(): bool + { + return $this->success; + } + + public function getVideo(): ?Video + { + return $this->video; + } + + public function getException(): ?Throwable + { + return $this->exception; + } +} \ No newline at end of file diff --git a/src/Infrastructure/Database/config/service_manager.php b/src/Infrastructure/Database/config/service_manager.php new file mode 100644 index 0000000..6bcf53d --- /dev/null +++ b/src/Infrastructure/Database/config/service_manager.php @@ -0,0 +1,11 @@ + [ + ], +]; diff --git a/src/Infrastructure/Database/src/ConfigProvider.php b/src/Infrastructure/Database/src/ConfigProvider.php new file mode 100644 index 0000000..7311e61 --- /dev/null +++ b/src/Infrastructure/Database/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . './../config/service_manager.php', + ]; + } +} diff --git a/src/Infrastructure/Database/src/ConfigServiceFactory.php b/src/Infrastructure/Database/src/ConfigServiceFactory.php new file mode 100644 index 0000000..c444383 --- /dev/null +++ b/src/Infrastructure/Database/src/ConfigServiceFactory.php @@ -0,0 +1,33 @@ +entityClass, $this->entityClass); + } + + public function __invoke( + ContainerInterface $container, + $requestedName, + ?array $options = null + ): ObjectRepository|EntityRepository + { + /** @var EntityManager $em */ + $em = $container->get($this->entityManagerClass); + + return $em->getRepository($this->entityClass); + } +} diff --git a/src/Infrastructure/DependencyInjection/config/service_manager.php b/src/Infrastructure/DependencyInjection/config/service_manager.php new file mode 100644 index 0000000..d289967 --- /dev/null +++ b/src/Infrastructure/DependencyInjection/config/service_manager.php @@ -0,0 +1,12 @@ + [ + ConfigService::class => ConfigServiceFactory::class, + ], +]; diff --git a/src/Infrastructure/DependencyInjection/src/ConfigProvider.php b/src/Infrastructure/DependencyInjection/src/ConfigProvider.php new file mode 100644 index 0000000..b49594c --- /dev/null +++ b/src/Infrastructure/DependencyInjection/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . './../config/service_manager.php', + ]; + } +} diff --git a/src/Infrastructure/DependencyInjection/src/ConfigServiceFactory.php b/src/Infrastructure/DependencyInjection/src/ConfigServiceFactory.php new file mode 100644 index 0000000..431d1dc --- /dev/null +++ b/src/Infrastructure/DependencyInjection/src/ConfigServiceFactory.php @@ -0,0 +1,21 @@ +get('config') + ) + ); + } +} diff --git a/src/Infrastructure/Encryption/config/service_manager.php b/src/Infrastructure/Encryption/config/service_manager.php new file mode 100644 index 0000000..05c6692 --- /dev/null +++ b/src/Infrastructure/Encryption/config/service_manager.php @@ -0,0 +1,12 @@ + [ + EncryptionClient::class => AutoWiringFactory::class, + ], +]; diff --git a/src/Infrastructure/Encryption/src/Client/EncryptionClient.php b/src/Infrastructure/Encryption/src/Client/EncryptionClient.php new file mode 100644 index 0000000..c489fc1 --- /dev/null +++ b/src/Infrastructure/Encryption/src/Client/EncryptionClient.php @@ -0,0 +1,26 @@ +bcrypt = new Bcrypt(); + $this->bcrypt->setCost(10); + } + + public function encrypt(string $value): string { + return $this->bcrypt->create($value); + } + + public function verify(string $value, string $encryptedValue): bool { + return $this->bcrypt->verify($value, $encryptedValue); + } +} diff --git a/src/Infrastructure/Encryption/src/ConfigProvider.php b/src/Infrastructure/Encryption/src/ConfigProvider.php new file mode 100644 index 0000000..78a1bb4 --- /dev/null +++ b/src/Infrastructure/Encryption/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . './../config/service_manager.php', + ]; + } +} diff --git a/src/Infrastructure/Exception/config/service_manager.php b/src/Infrastructure/Exception/config/service_manager.php new file mode 100644 index 0000000..86d66ec --- /dev/null +++ b/src/Infrastructure/Exception/config/service_manager.php @@ -0,0 +1,12 @@ + [ + MyTubeExceptionHandlerMiddleware::class => AutoWiringFactory::class, + ], +]; diff --git a/src/Infrastructure/Exception/src/ConfigProvider.php b/src/Infrastructure/Exception/src/ConfigProvider.php new file mode 100644 index 0000000..4743811 --- /dev/null +++ b/src/Infrastructure/Exception/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . './../config/service_manager.php', + ]; + } +} diff --git a/src/Infrastructure/Exception/src/ErrorCode.php b/src/Infrastructure/Exception/src/ErrorCode.php new file mode 100644 index 0000000..45f6947 --- /dev/null +++ b/src/Infrastructure/Exception/src/ErrorCode.php @@ -0,0 +1,12 @@ +errorCode = $code; + $this->errorDomain = $domain; + + parent::__construct( + $message, + 0, + $previous + ); + } + + public function getErrorDomain(): ErrorDomain { + return $this->errorDomain; + } + + public function getErrorCode(): ErrorCode { + return $this->errorCode; + } +} \ No newline at end of file diff --git a/src/Infrastructure/Exception/src/Middleware/MyTubeExceptionHandlerMiddleware.php b/src/Infrastructure/Exception/src/Middleware/MyTubeExceptionHandlerMiddleware.php new file mode 100644 index 0000000..90f409a --- /dev/null +++ b/src/Infrastructure/Exception/src/Middleware/MyTubeExceptionHandlerMiddleware.php @@ -0,0 +1,48 @@ +handle($request); + } catch (MyTubeException $exception) { + $this->logger->exception($exception); + + return new ErrorResponse( + $exception->getErrorDomain(), + $exception->getErrorCode(), + $exception->getMessage() + ); + } catch (Throwable $exception) { + $this->logger->exception($exception); + + return new ErrorResponse( + ErrorDomain::Generic, + ErrorCode::SomethingWentWrong, + $exception->getMessage() + ); + } + } +} diff --git a/src/Infrastructure/Logging/config/service_manager.php b/src/Infrastructure/Logging/config/service_manager.php new file mode 100644 index 0000000..b9a6d73 --- /dev/null +++ b/src/Infrastructure/Logging/config/service_manager.php @@ -0,0 +1,23 @@ + [ + MyTubeLogger::class => LoggerFactory::class, + Logger::class => MonologLoggerFactory::class, + FileStreamHandler::class => FileStreamHandlerFactory::class, + DoctrineLogHandler::class => AutoWiringFactory::class, + PsrLogMessageProcessor::class => AutoWiringFactory::class, + ], +]; diff --git a/src/Infrastructure/Logging/src/ConfigProvider.php b/src/Infrastructure/Logging/src/ConfigProvider.php new file mode 100644 index 0000000..7a2ddae --- /dev/null +++ b/src/Infrastructure/Logging/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . './../config/service_manager.php', + ]; + } +} diff --git a/src/Infrastructure/Logging/src/Factory/FileStreamHandlerFactory.php b/src/Infrastructure/Logging/src/Factory/FileStreamHandlerFactory.php new file mode 100644 index 0000000..2273ab3 --- /dev/null +++ b/src/Infrastructure/Logging/src/Factory/FileStreamHandlerFactory.php @@ -0,0 +1,29 @@ +get(ConfigService::class)->resolve('logger'); + + return new FileStreamHandler( + $config['path'] ?? '', + $config['level'] ?? Level::Debug, + $config['pretty'] ?? false, + ); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Logging/src/Factory/LoggerFactory.php b/src/Infrastructure/Logging/src/Factory/LoggerFactory.php new file mode 100644 index 0000000..9f9f5ca --- /dev/null +++ b/src/Infrastructure/Logging/src/Factory/LoggerFactory.php @@ -0,0 +1,22 @@ +get(Logger::class) + ); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Logging/src/Factory/MonologLoggerFactory.php b/src/Infrastructure/Logging/src/Factory/MonologLoggerFactory.php new file mode 100644 index 0000000..fff8fb0 --- /dev/null +++ b/src/Infrastructure/Logging/src/Factory/MonologLoggerFactory.php @@ -0,0 +1,34 @@ +get(ConfigService::class)->resolve('logger'); + + $logfileHandler = $container->get(FileStreamHandler::class); + $databaseHandler = $container->get(DoctrineLogHandler::class); + + return new Logger( + $config['name'] ?? '', + [$logfileHandler, $databaseHandler], + [$container->get(PsrLogMessageProcessor::class)], + ); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Logging/src/Formatter/PrettyFileLogLines.php b/src/Infrastructure/Logging/src/Formatter/PrettyFileLogLines.php new file mode 100644 index 0000000..ab7361e --- /dev/null +++ b/src/Infrastructure/Logging/src/Formatter/PrettyFileLogLines.php @@ -0,0 +1,77 @@ +level; + $normalizedRecord = parent::format($record); + $timeStamp = (new DateTimeImmutable())->format(DateTimeInterface::ATOM); + + $lines = ["\n[" . $timeStamp . '] ' . $level->getName() . ' >> ' . $normalizedRecord['message']]; + + foreach ($normalizedRecord['context'] ?? [] as $name => $payload) { + if (is_string($payload) && in_array(substr($payload, 0, 1), ['[', '{'])) { + $json = json_decode($payload, true); + } elseif (is_array($payload) || is_object($payload)) { + $json = $payload; + } else { + if (!$payload) { + $payload = ''; + } + $lines[] = "\t" . $name . ':' . $payload; + continue; + } + + try { + $secondLevelLines = []; + + $i = 0; + foreach ((array) $json as $secondLevelName => $secondLevelPayload) { + if (!is_string($secondLevelPayload)) { + $secondLevelPayload = json_encode($secondLevelPayload); + } + + $secondLevelLines[] = $i == 0 ? + "\t" . $name . ":\t" . $secondLevelName . ': ' . $secondLevelPayload : + "\t\t" . $secondLevelName . ': ' . $secondLevelPayload; + + $i++; + } + + foreach ($secondLevelLines as $line) { + $lines[] = $line; + } + } catch (Exception) { + $lines[] = '\t' . $name . ': ' . print_r($json, true); + } + } + + $string = implode(PHP_EOL, $lines); + + return "\n\033[" . $this->colors($level) . 'm' . $string . "\033[0m"; + } + + private function colors(Level $level): string + { + return match($level) { + Level::Debug => '0;2', + Level::Info => '0;32', + Level::Notice => '0;33', + Level::Warning => '0;33', + Level::Error => '0;31', + Level::Critical => '0;31', + Level::Alert => '0;31', + Level::Emergency => '0;31', + }; + } +} diff --git a/src/Infrastructure/Logging/src/Handler/DoctrineLogHandler.php b/src/Infrastructure/Logging/src/Handler/DoctrineLogHandler.php new file mode 100644 index 0000000..54e01e8 --- /dev/null +++ b/src/Infrastructure/Logging/src/Handler/DoctrineLogHandler.php @@ -0,0 +1,37 @@ +logEntityManager = $logEntityManager; + parent::__construct(); + } + + public function write(LogRecord $record): void + { + $log = new Log(); + + $log->setMessage($record->message); + $log->setLevel($record->level->value); + $log->setLevelName($record->level->getName()); + $log->setExtra($record->extra); + $log->setContext($record->context); + $log->setCreatedAt(new DateTime('now')); + + $this->logEntityManager->persist($log); + $this->logEntityManager->flush(); + } +} diff --git a/src/Infrastructure/Logging/src/Handler/FileStreamHandler.php b/src/Infrastructure/Logging/src/Handler/FileStreamHandler.php new file mode 100644 index 0000000..141b015 --- /dev/null +++ b/src/Infrastructure/Logging/src/Handler/FileStreamHandler.php @@ -0,0 +1,21 @@ +setFormatter(new PrettyFileLogLines('y-m-D')); + } + } +} diff --git a/src/Infrastructure/Logging/src/Logger/Logger.php b/src/Infrastructure/Logging/src/Logger/Logger.php new file mode 100644 index 0000000..752da59 --- /dev/null +++ b/src/Infrastructure/Logging/src/Logger/Logger.php @@ -0,0 +1,91 @@ + $exception->getTraceAsString() + ]; + + if ($exception instanceof MyTubeException) { + $exceptionContext = array_merge([ + 'errorDomain' => $exception->getErrorDomain()->value, + 'errorCode' => $exception->getErrorCode()->value, + ], $exceptionContext); + } + + $this->monologLogger->error( + $message ?: $exception->getMessage(), + $this->getMergedContexts($exceptionContext) + ); + } + + private function getMergedContexts(array $context): array + { + return array_merge($this->defaultContext, $context); + } + + public function emergency(Stringable|string $message, array $context = []): void + { + $this->monologLogger->emergency($message, $this->getMergedContexts($context)); + } + + public function alert(Stringable|string $message, array $context = []): void + { + $this->monologLogger->alert($message, $this->getMergedContexts($context)); + } + + public function critical(Stringable|string $message, array $context = []): void + { + $this->monologLogger->critical($message, $this->getMergedContexts($context)); + } + + public function error(Stringable|string $message, array $context = []): void + { + $this->monologLogger->error($message, $this->getMergedContexts($context)); + } + + public function warning(Stringable|string $message, array $context = []): void + { + $this->monologLogger->warning($message, $this->getMergedContexts($context)); + } + + public function notice(Stringable|string $message, array $context = []): void + { + $this->monologLogger->notice($message, $this->getMergedContexts($context)); + } + + public function info(Stringable|string $message, array $context = []): void + { + $this->monologLogger->info($message, $this->getMergedContexts($context)); + } + + public function debug(Stringable|string $message, array $context = []): void + { + $this->monologLogger->debug($message, $this->getMergedContexts($context)); + } + + public function log($level, Stringable|string $message, array $context = []): void + { + $this->monologLogger->log($level, $message, $this->getMergedContexts($context)); + } +} diff --git a/src/Infrastructure/Rbac/config/service_manager.php b/src/Infrastructure/Rbac/config/service_manager.php new file mode 100644 index 0000000..7dd958c --- /dev/null +++ b/src/Infrastructure/Rbac/config/service_manager.php @@ -0,0 +1,12 @@ + [ + EnsureAuthorizationMiddleware::class => InjectionFactory::class + ], +]; diff --git a/src/Infrastructure/Rbac/src/ConfigProvider.php b/src/Infrastructure/Rbac/src/ConfigProvider.php new file mode 100644 index 0000000..65ae52e --- /dev/null +++ b/src/Infrastructure/Rbac/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . './../config/service_manager.php', + ]; + } +} diff --git a/src/Infrastructure/Rbac/src/Middleware/EnsureAuthorizationMiddleware.php b/src/Infrastructure/Rbac/src/Middleware/EnsureAuthorizationMiddleware.php new file mode 100644 index 0000000..8fc7a99 --- /dev/null +++ b/src/Infrastructure/Rbac/src/Middleware/EnsureAuthorizationMiddleware.php @@ -0,0 +1,70 @@ +toArray() as $route) { + $path = $route['path']; + $permission = $route['name']; + $this->routes[$path] = $permission; + } + } + + public function process( + ServerRequestInterface $request, + RequestHandlerInterface $handler + ): ResponseInterface + { + /** @var User $user */ + $user = $request->getAttribute(LoggedInUserMiddleware::USER_KEY); + + if ( + !$this->checkRights( + $request->getRequestTarget(), + $user + ) + ) { + return new ForbiddenResponse(); + } + + return $handler->handle($request); + } + + private function checkRights( + string $targetPath, + User $user + ): bool { + $role = $user->getRole(); + $permissions = $role->getPermissions(); + $targetApi = $this->routes[$targetPath]; + + /** @var Permission $permission */ + foreach($permissions as $permission) { + if ($permission->getIdentifier() === $targetApi) { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/src/Infrastructure/Request/config/service_manager.php b/src/Infrastructure/Request/config/service_manager.php new file mode 100644 index 0000000..4f540e3 --- /dev/null +++ b/src/Infrastructure/Request/config/service_manager.php @@ -0,0 +1,22 @@ + [ + /// Service + RequestService::class => RequestServiceFactory::class, + + /// Middleware + AnalyzeHeaderMiddleware::class => AutoWiringFactory::class, + InternalRequestMiddleware::class => AutoWiringFactory::class, + ], +]; diff --git a/src/Infrastructure/Request/src/ConfigProvider.php b/src/Infrastructure/Request/src/ConfigProvider.php new file mode 100644 index 0000000..ee61840 --- /dev/null +++ b/src/Infrastructure/Request/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . './../config/service_manager.php', + ]; + } +} diff --git a/src/Infrastructure/Request/src/Exception/ApiIdentifierUnknownException.php b/src/Infrastructure/Request/src/Exception/ApiIdentifierUnknownException.php new file mode 100644 index 0000000..bbf6692 --- /dev/null +++ b/src/Infrastructure/Request/src/Exception/ApiIdentifierUnknownException.php @@ -0,0 +1,22 @@ +getMessage() + ), + 0, + $previous + ); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Request/src/Exception/ApiServiceUnknownException.php b/src/Infrastructure/Request/src/Exception/ApiServiceUnknownException.php new file mode 100644 index 0000000..4dc94cb --- /dev/null +++ b/src/Infrastructure/Request/src/Exception/ApiServiceUnknownException.php @@ -0,0 +1,20 @@ +get(ConfigService::class); + $apiKey = $configService->resolve("api.keys.myTube"); + + return new RequestService( + $apiKey, + $configService + ); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Request/src/Middleware/AnalyzeBodyMiddleware.php b/src/Infrastructure/Request/src/Middleware/AnalyzeBodyMiddleware.php new file mode 100644 index 0000000..31d22cc --- /dev/null +++ b/src/Infrastructure/Request/src/Middleware/AnalyzeBodyMiddleware.php @@ -0,0 +1,43 @@ +getHeaderLine(self::CONTENT_TYPE_HEADER); + + if (str_contains($contentType, self::CONTENT_TYPE_APPLICATION_JSON)) { + $jsonData = json_decode( + $request->getBody()->getContents(), + true + ); + } + + return $handler->handle($request->withAttribute( + self::JSON_DATA, + $jsonData + )); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Request/src/Middleware/AnalyzeHeaderMiddleware.php b/src/Infrastructure/Request/src/Middleware/AnalyzeHeaderMiddleware.php new file mode 100644 index 0000000..5f5f1e2 --- /dev/null +++ b/src/Infrastructure/Request/src/Middleware/AnalyzeHeaderMiddleware.php @@ -0,0 +1,37 @@ +getHeaders()['host'][0] + ?? $request->getHeaders()['x-forwarded-host'][0] + ?? 'UNKNOWN' + )[0]; + + return $handler->handle($request->withAttribute( + self::HOST_ATTRIBUTE, + $host + )); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Request/src/Middleware/InternalRequestMiddleware.php b/src/Infrastructure/Request/src/Middleware/InternalRequestMiddleware.php new file mode 100644 index 0000000..560e809 --- /dev/null +++ b/src/Infrastructure/Request/src/Middleware/InternalRequestMiddleware.php @@ -0,0 +1,47 @@ +apiKeys = $this->configService->resolve('apiKeys')->toArray(); + } + + public function process( + ServerRequestInterface $request, + RequestHandlerInterface $handler + ): ResponseInterface { + $requestApiKey = $request->getHeader(self::API_KEY_HEADER)[0] ?? null; + + if ($requestApiKey === null) { + return new UnauthorizedResponse(); + } + + foreach ($this->apiKeys as $application => $apiKey) { + if ($apiKey === $requestApiKey) + return $handler->handle( + $request->withAttribute( + self::APPLICATION_KEY, + $application + ) + ); + } + + return new UnauthorizedResponse(); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Request/src/Service/RequestService.php b/src/Infrastructure/Request/src/Service/RequestService.php new file mode 100644 index 0000000..6d321c3 --- /dev/null +++ b/src/Infrastructure/Request/src/Service/RequestService.php @@ -0,0 +1,95 @@ +client = new Client(); + $this->services = $this->configService->resolve('api.services')->toArray(); + } + + /** + * @throws ApiRequestFailedException + * @throws ApiIdentifierUnknownException + * @throws ApiPropertyUndefinedException + * @throws ApiServiceUnknownException + */ + public function request( + string $serviceIdentifier, + string $apiIdentifier, + ?array $data + ): ResponseInterface { + $serviceConfig = $this->services[$serviceIdentifier] ?? throw new ApiServiceUnknownException($serviceIdentifier); + $apiHost = $serviceConfig['host']; + $apiConfig = $serviceConfig['apis'][$apiIdentifier] ?? throw new ApiIdentifierUnknownException($serviceIdentifier, $apiIdentifier); + + + $method = $apiConfig['method'] ?? + throw new ApiPropertyUndefinedException( + $serviceIdentifier, + $apiIdentifier, + 'method' + ); + + $url = $apiHost . $apiConfig['path'] ?? + throw new ApiPropertyUndefinedException( + $serviceIdentifier, + $apiIdentifier, + 'path' + ); + + return $this->sendRequest( + $method, + $url, + $data + ); + } + + private function sendRequest( + string $method, + string $url, + ?array $data + ): ResponseInterface { + $options = [ + 'headers' => [ + InternalRequestMiddleware::API_KEY_HEADER => $this->apiKey + ] + ]; + + if (!empty($data)) { + $options['body'] = json_encode($data); + $options['headers']['Content-Type'] = 'application/json'; + } + + try { + return $this->client->request( + $method, + $url, + $options + ); + } catch (\Throwable $e) { + throw new ApiRequestFailedException( + $url, + $method, + $e + ); + } + } +} diff --git a/src/Infrastructure/Response/src/ErrorResponse.php b/src/Infrastructure/Response/src/ErrorResponse.php new file mode 100644 index 0000000..6907f8b --- /dev/null +++ b/src/Infrastructure/Response/src/ErrorResponse.php @@ -0,0 +1,37 @@ + [ + 'code' => sprintf( + self::ERROR_CODE, + $domain->value, + $code->value + ) + ] + ]; + + if ($message !== null) { + $response['error']['message'] = $message; + } + + parent::__construct( + $response, + 400 + ); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Response/src/ForbiddenResponse.php b/src/Infrastructure/Response/src/ForbiddenResponse.php new file mode 100644 index 0000000..87d8611 --- /dev/null +++ b/src/Infrastructure/Response/src/ForbiddenResponse.php @@ -0,0 +1,16 @@ + [ + SessionMiddleware::class => AutoWiringFactory::class, + LoggedInUserMiddleware::class => AutoWiringFactory::class + ], +]; diff --git a/src/Infrastructure/Session/src/ConfigProvider.php b/src/Infrastructure/Session/src/ConfigProvider.php new file mode 100644 index 0000000..437da12 --- /dev/null +++ b/src/Infrastructure/Session/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . './../config/service_manager.php', + ]; + } +} diff --git a/src/Infrastructure/Session/src/Middleware/LoggedInUserMiddleware.php b/src/Infrastructure/Session/src/Middleware/LoggedInUserMiddleware.php new file mode 100644 index 0000000..ae44555 --- /dev/null +++ b/src/Infrastructure/Session/src/Middleware/LoggedInUserMiddleware.php @@ -0,0 +1,41 @@ +getAttribute( + SessionMiddleware::SESSION_ATTRIBUTE + ) ?? null; + + if ($session === null) { + return new UnauthorizedResponse(); + } + + if ($session->getUser() === null) { + return new UnauthorizedResponse(); + } + + return $handler->handle( + $request->withAttribute( + self::USER_KEY, + $session->getUser() + ) + ); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Session/src/Middleware/SessionMiddleware.php b/src/Infrastructure/Session/src/Middleware/SessionMiddleware.php new file mode 100644 index 0000000..cd3947a --- /dev/null +++ b/src/Infrastructure/Session/src/Middleware/SessionMiddleware.php @@ -0,0 +1,81 @@ +userSessionRepository = $this->entityManager->getRepository(UserSession::class); + } + + public function process( + ServerRequestInterface $request, + RequestHandlerInterface $handler + ): ResponseInterface + { + /** @var ?UserSession $session */ + $session = null; + $sessionId = null; + $cookies = []; + $cookieHeaders = $request->getHeaders()['cookie'] ?? []; + + foreach ($cookieHeaders as $cookie) { + $properties = explode(';', $cookie); + + foreach ($properties as $property) { + $keyValuePair = explode('=', $property); + $cookies[trim($keyValuePair[0])] = trim($keyValuePair[1]); + } + } + + if (isset($cookies[self::SESSION_COOKIE_NAME])) { + $sessionId = $cookies[self::SESSION_COOKIE_NAME]; + } + + if ($sessionId !== null) { + $session = $this->userSessionRepository->findOneBy([ + 'id' => $sessionId + ]) ?? null; + } + + if($session === null) { + $session = $this->userSessionBuilder->build(); + + $this->entityManager->persist($session); + $this->entityManager->flush(); + } + + $response = $handler->handle( + $request->withAttribute( + self::SESSION_ATTRIBUTE, + $session + ) + ); + + return $response->withAddedHeader( + 'Set-Cookie', + sprintf( + "%s=%s; Path=/", + self::SESSION_COOKIE_NAME, + $session->getId()->toString() + ) + ); + } +} \ No newline at end of file diff --git a/src/Infrastructure/UuidGenerator/src/UuidGenerator.php b/src/Infrastructure/UuidGenerator/src/UuidGenerator.php new file mode 100644 index 0000000..c4c6226 --- /dev/null +++ b/src/Infrastructure/UuidGenerator/src/UuidGenerator.php @@ -0,0 +1,18 @@ +getUuidBuilder()); + $factory->setCodec($codec); + return $factory->uuid1(); + } +} + +?> \ No newline at end of file