├── .github └── workflows │ ├── docs.yaml │ └── main.yml ├── .gitignore ├── .golangci.yml ├── License ├── Makefile ├── README.md ├── codecov.yml ├── datalayer └── orm.go ├── docs ├── .gitignore ├── docs │ ├── .vuepress │ │ ├── config.js │ │ ├── config │ │ │ ├── algolia.js │ │ │ ├── index.js │ │ │ ├── nav.js │ │ │ ├── search.js │ │ │ └── sidebar.js │ │ └── public │ │ │ ├── logo-favicon-90x90.png │ │ │ └── logo-favicon.png │ ├── README.md │ ├── guide │ │ ├── features │ │ │ ├── acl.md │ │ │ ├── consumer_runners.md │ │ │ ├── flags.md │ │ │ ├── goroutine.md │ │ │ ├── helper.md │ │ │ ├── pagination.md │ │ │ ├── script.md │ │ │ ├── seeder.md │ │ │ ├── test.md │ │ │ ├── upload_files.md │ │ │ └── validator.md │ │ ├── graphql │ │ │ └── dataloaders.md │ │ └── services │ │ │ ├── api_logger.md │ │ │ ├── app.md │ │ │ ├── authentication.md │ │ │ ├── checkout.md │ │ │ ├── clock.md │ │ │ ├── clockwork.md │ │ │ ├── config.md │ │ │ ├── crud.md │ │ │ ├── ddos.md │ │ │ ├── dynamic_link.md │ │ │ ├── elorus.md │ │ │ ├── error_logger.md │ │ │ ├── exporter.md │ │ │ ├── fcm.md │ │ │ ├── feature_flag.md │ │ │ ├── file_extractor.md │ │ │ ├── geocoding.md │ │ │ ├── google_analytics.md │ │ │ ├── gql.md │ │ │ ├── html2pdf.md │ │ │ ├── jwt.md │ │ │ ├── kubernetes.md │ │ │ ├── license_plate_recognizer.md │ │ │ ├── localizer.md │ │ │ ├── mail.md │ │ │ ├── orm_engine.md │ │ │ ├── orm_engine_context.md │ │ │ ├── oss.md │ │ │ ├── otp.md │ │ │ ├── password.md │ │ │ ├── request_logger.md │ │ │ ├── sentry.md │ │ │ ├── setting.md │ │ │ ├── slack.md │ │ │ ├── sms.md │ │ │ ├── stripe.md │ │ │ ├── template.md │ │ │ ├── uploader.md │ │ │ └── websocket.md │ ├── roadmap │ │ └── README.md │ └── rules │ │ └── README.md ├── package.json └── yarn.lock ├── example ├── cmd │ ├── server │ │ └── main.go │ └── single-instance-cron │ │ └── main.go ├── config │ ├── .env.local │ ├── .env.local.sample │ ├── .env.test │ ├── .env.test.sample │ ├── hitrix.yaml │ ├── server │ │ └── config.yaml │ └── single-instance-cron │ │ └── config.yaml ├── docker │ ├── .env │ ├── build.sh │ ├── docker-compose.yml │ ├── services.sh │ └── services │ │ ├── Dockerfile │ │ └── docker-entrypoint.sh ├── entity │ ├── admin_user_entity.go │ ├── api_logger_entity.go │ ├── dev_panel_user_entity.go │ └── initialize │ │ └── init.go ├── gqlgen.yml ├── graph │ ├── generated │ │ └── generated.go │ ├── model │ │ └── models_gen.go │ ├── resolver.go │ ├── schema.graphqls │ └── schema.resolvers.go ├── model │ └── socket │ │ └── registry.go ├── oss │ └── oss.go ├── redis │ └── const.go ├── redis_search │ └── dev_panel_user.go ├── redis_stream │ └── consumers │ │ └── dirty_consumer.go ├── rest │ ├── controller │ │ └── websocket_controller.go │ └── middleware │ │ └── router.go ├── scripts │ └── dirty_consumer_script.go └── websocket-demo.html ├── gin.go ├── go.mod ├── go.sum ├── goroutine.go ├── pkg ├── binding │ ├── json.go │ └── validator.go ├── controller │ ├── acl_controller.go │ ├── clockwork_controller.go │ ├── dev_panel_controller.go │ ├── error_log_controller.go │ ├── file_controller.go │ ├── readiness_controller.go │ ├── translation_controller.go │ └── uploader_controller.go ├── cookie_store │ └── cookie_store.go ├── dto │ ├── acl │ │ ├── permission.go │ │ ├── resource.go │ │ └── role.go │ ├── file │ │ ├── file.go │ │ └── image.go │ ├── indexes │ │ └── indexes.go │ ├── list │ │ └── list.go │ ├── requestlogger │ │ └── request_logger.go │ └── translation │ │ └── translation.go ├── entity │ ├── feature_flag_entity.go │ ├── file_entity.go │ ├── geocoding_entity.go │ ├── geocoding_reverse_entity.go │ ├── language_enum.go │ ├── mail_tracker_entity.go │ ├── oss_bucket_entity.go │ ├── otp_sms_tracker_entity.go │ ├── permission_entity.go │ ├── privilege_entity.go │ ├── request_logger_entity.go │ ├── resource_entity.go │ ├── role_entity.go │ ├── seeder_entity.go │ ├── settings_entity.go │ ├── sms_tracker_entity.go │ └── translation_text_entity.go ├── error_handling │ └── error_handling.go ├── errors │ └── errors.go ├── graphql │ ├── depth │ │ └── depth.go │ └── scalars │ │ ├── json.go │ │ ├── mapUint64.go │ │ └── uint64.go ├── helper │ ├── array.go │ ├── array_test.go │ ├── auth.go │ ├── call.go │ ├── cdn │ │ └── cdn.go │ ├── file.go │ ├── float_precision.go │ ├── geo.go │ ├── helper.go │ ├── mysql.go │ ├── pagination.go │ ├── phone.go │ ├── price.go │ ├── price_test.go │ ├── time.go │ └── time_test.go ├── middleware │ ├── acl_router.go │ ├── clockwork.go │ ├── cors.go │ ├── dev_auth.go │ ├── dev_panel_router.go │ ├── file_router.go │ ├── request_logger.go │ └── url_auth.go ├── model │ ├── account │ │ └── login_dev.go │ ├── acl │ │ └── role.go │ ├── file │ │ └── create.go │ └── translation │ │ ├── create.go │ │ ├── delete.go │ │ └── update.go ├── queue │ ├── consumer.go │ ├── consumers │ │ ├── otp_retry.go │ │ └── redisearch_reindex.go │ └── streams │ │ ├── streams.go │ │ └── streams_test.go ├── response │ └── response.go ├── test │ ├── graphql-parser │ │ ├── error.go │ │ ├── graphql.go │ │ ├── ident │ │ │ └── graphql.go │ │ └── query.go │ └── test.go └── view │ ├── account │ └── dev_token.go │ ├── acl │ ├── acl.go │ ├── resource.go │ └── role.go │ ├── crud │ └── validate.go │ ├── file │ └── file.go │ ├── requestlogger │ └── request_logger.go │ └── translation │ ├── get.go │ └── list.go ├── recovery.go ├── registry.go ├── script.go ├── scripts ├── clear_expired_geocoding_cache.go ├── orm_alters.go ├── redisearch_reindex_consumer.go ├── retry_otp_consumer.go └── seed_db.go ├── server.go ├── service ├── component │ ├── amazon │ │ ├── mocks │ │ │ └── s3.go │ │ └── storage │ │ │ └── s3.go │ ├── api_logger │ │ ├── api_logger.go │ │ └── mysql.go │ ├── app │ │ ├── app_definition.go │ │ ├── flags.go │ │ └── script.go │ ├── authentication │ │ └── authentication.go │ ├── calendar │ │ ├── calendar.go │ │ ├── google_calendar.go │ │ └── mocks │ │ │ └── google_calendar.go │ ├── checkout │ │ ├── checkout.go │ │ ├── checkout_sdk.go │ │ ├── customer.go │ │ └── mocks │ │ │ └── checkout.go │ ├── clock │ │ ├── clock.go │ │ ├── mocks │ │ │ └── clock.go │ │ └── sysclock.go │ ├── config │ │ └── config.go │ ├── crud │ │ ├── const.go │ │ ├── crud_test.go │ │ └── list.go │ ├── ddos │ │ └── ddos.go │ ├── dynamic_link │ │ ├── dynamic_link.go │ │ ├── firebase │ │ │ └── dynamic_link.go │ │ └── mocks │ │ │ └── dynamic_link.go │ ├── elorus │ │ ├── elorus.go │ │ ├── elorus_provider.go │ │ └── mocks │ │ │ └── elorus.go │ ├── error_logger │ │ └── error_logger.go │ ├── exporter │ │ ├── csv.go │ │ ├── excel.go │ │ ├── exporter.go │ │ └── mocks │ │ │ └── exporter.go │ ├── fcm │ │ ├── fcm.go │ │ └── mocks │ │ │ └── fcm.go │ ├── feature_flag │ │ ├── feature_flag.go │ │ ├── feature_flag_mysql.go │ │ ├── feature_flag_with_cache.go │ │ └── mock │ │ │ └── mock.go │ ├── file_extractor │ │ └── file_extractor.go │ ├── generator │ │ ├── generator.go │ │ ├── mocks │ │ │ └── generator.go │ │ ├── simple.go │ │ └── simple_test.go │ ├── geocoding │ │ ├── geocoding.go │ │ ├── google_maps_provider.go │ │ ├── language_constants.go │ │ ├── mocks │ │ │ └── geocoding.go │ │ └── provider.go │ ├── google_analytics │ │ ├── ga4.go │ │ ├── google_analytics.go │ │ ├── google_analytics_provider.go │ │ └── mocks │ │ │ ├── ga4.go │ │ │ └── google_analytics.go │ ├── goroutine │ │ ├── goroutine.go │ │ └── manager.go │ ├── gql │ │ └── gql.go │ ├── html2pdf │ │ ├── html2pdf.go │ │ └── mocks │ │ │ └── html2pdf.go │ ├── instagram │ │ ├── instagram.go │ │ ├── mocks │ │ │ ├── fakeRapidAPIInstagram.go │ │ │ └── instagram.go │ │ ├── rapidAPIInstagram188.go │ │ ├── rapidAPIInstagram28.go │ │ └── rapidAPIInstagram85.go │ ├── jwt │ │ └── jwt.go │ ├── kubernetes │ │ ├── kubernetes.go │ │ ├── kubernetes_provider.go │ │ └── mocks │ │ │ └── kubernetes.go │ ├── license_plate_recognizer │ │ ├── license_plate_recognizer.go │ │ ├── mocks │ │ │ └── license_plate_recognizer.go │ │ ├── moskvich.jpg │ │ ├── platerecognizer.go │ │ └── platerecognizer_test.go │ ├── localize │ │ ├── localizer.go │ │ ├── poeditor.go │ │ └── source.go │ ├── mail │ │ ├── mailjet_sender.go │ │ ├── mandrill_sender.go │ │ ├── mocks │ │ │ └── sender.go │ │ ├── provider.go │ │ └── sender.go │ ├── oss │ │ ├── amazon_oss.go │ │ ├── google_oss.go │ │ ├── mocks │ │ │ └── oss.go │ │ └── oss.go │ ├── otp │ │ ├── mada_gateway.go │ │ ├── mocks │ │ │ ├── gateway.go │ │ │ └── otp.go │ │ ├── otp.go │ │ ├── sinch_gateway.go │ │ ├── sms_provider.go │ │ └── twilio_gateway.go │ ├── password │ │ ├── password.go │ │ ├── simple.go │ │ └── simple_test.go │ ├── request_logger │ │ ├── db_logger.go │ │ └── request_logger.go │ ├── sentry │ │ ├── mocks │ │ │ └── sentry.go │ │ └── sentry.go │ ├── setting │ │ ├── mock │ │ │ └── mock.go │ │ ├── setting.go │ │ └── setting_mysql.go │ ├── slack │ │ ├── slack.go │ │ └── slack_go.go │ ├── sms │ │ ├── kavenegar_provider.go │ │ ├── link_mobility_provider.go │ │ ├── log.go │ │ ├── mobica_provider.go │ │ ├── mocks │ │ │ └── sms.go │ │ ├── provider.go │ │ ├── sender.go │ │ ├── sinch_provider.go │ │ └── twilio_provider.go │ ├── social │ │ ├── apple.go │ │ ├── facebook.go │ │ ├── google.go │ │ ├── mocks │ │ │ └── social.go │ │ └── social.go │ ├── socket │ │ ├── registry.go │ │ └── socket.go │ ├── stripe │ │ ├── mocks │ │ │ └── stripe.go │ │ └── stripe.go │ ├── template │ │ ├── mocks │ │ │ └── template.go │ │ └── template.go │ ├── translation │ │ ├── mocks │ │ │ └── translation.go │ │ └── translaton.go │ ├── uploader │ │ ├── amazon_store.go │ │ ├── google_store.go │ │ ├── locker │ │ │ ├── locker.go │ │ │ └── redis.go │ │ ├── store.go │ │ └── uploader.go │ └── uuid │ │ ├── google.go │ │ ├── mocks │ │ └── uuid.go │ │ └── uuid.go ├── container.go ├── registry │ ├── api_logger.go │ ├── app_definition.go │ ├── authentication.go │ ├── calendar.go │ ├── checkout.go │ ├── clock.go │ ├── clockwork.go │ ├── config.go │ ├── crud.go │ ├── ddos.go │ ├── dynamic_link.go │ ├── elorus.go │ ├── error_logger.go │ ├── exporter.go │ ├── fcm.go │ ├── feature_flag.go │ ├── file_extractor.go │ ├── generator.go │ ├── geocoding.go │ ├── google_analytics.go │ ├── goroutine.go │ ├── gql.go │ ├── html2pdf.go │ ├── instagram.go │ ├── jwt.go │ ├── kubernetes.go │ ├── license_plate_recognizer.go │ ├── localize.go │ ├── mail.go │ ├── mocks │ │ ├── calendar.go │ │ ├── checkout.go │ │ ├── clock.go │ │ ├── dynamic_link.go │ │ ├── elorus.go │ │ ├── exporter.go │ │ ├── fcm.go │ │ ├── feature_flag.go │ │ ├── generator.go │ │ ├── geocoding.go │ │ ├── google_analytics.go │ │ ├── instagram.go │ │ ├── kubernetes.go │ │ ├── license_plate_recognizer.go │ │ ├── mail.go │ │ ├── oss.go │ │ ├── otp.go │ │ ├── pdf.go │ │ ├── s3.go │ │ ├── sentry.go │ │ ├── setting.go │ │ ├── sms.go │ │ ├── stripe.go │ │ ├── template.go │ │ └── uuid.go │ ├── orm_config.go │ ├── orm_engine.go │ ├── oss.go │ ├── otp.go │ ├── password.go │ ├── request_logger.go │ ├── s3.go │ ├── sentry.go │ ├── setting.go │ ├── slack.go │ ├── sms.go │ ├── social.go │ ├── socket.go │ ├── stripe.go │ ├── template.go │ ├── translation.go │ ├── uploader.go │ └── uuid.go └── service.go ├── test ├── acl_controller_test.go ├── api_logger_test.go ├── authentication_test.go ├── exporter_test.go ├── feature_flag_test.go ├── file_controller_test.go ├── http.go ├── jwt_test.go ├── main.go ├── otp_retry_test.go ├── queries.go ├── redis_search_test.go ├── script_test.go ├── test.go └── validator_test.go └── validator.go /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: checks & tests 2 | 3 | on: push 4 | 5 | jobs: 6 | checks: 7 | name: Quality & Security checks 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Setup Go 11 | uses: actions/setup-go@v3 12 | with: 13 | go-version: 1.19 14 | 15 | - name: Checkout code 16 | uses: actions/checkout@v3 17 | 18 | - name: Run Linters 19 | uses: golangci/golangci-lint-action@v3.7.0 20 | with: 21 | version: v1.54.1 22 | args: --verbose 23 | tests: 24 | name: Tests with coverage 25 | runs-on: ubuntu-latest 26 | needs: checks 27 | services: 28 | redis: 29 | image: redislabs/redisearch:latest 30 | ports: 31 | - 9002:6379 32 | steps: 33 | - name: Setup Go 34 | uses: actions/setup-go@v3 35 | with: 36 | go-version: 1.19 37 | 38 | - name: Checkout code 39 | uses: actions/checkout@v3 40 | 41 | - name: Configure MySQL & Create databases 42 | run: | 43 | sudo bash -c 'echo -e "[mysqld]\nport=9004\n" >> /etc/mysql/my.cnf' 44 | sudo systemctl start mysql.service 45 | mysql -e 'CREATE DATABASE hitrix;' -uroot -proot 46 | 47 | - name: Run tests with coverage 48 | run: | 49 | make test-cover 50 | 51 | - name: Upload report to codecov 52 | uses: codecov/codecov-action@v3 53 | with: 54 | token: ${{ secrets.CODECOV_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | 3 | bin/ -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | enable: 3 | - forbidigo 4 | - goimports 5 | - gci 6 | - gocyclo 7 | - gosec 8 | - nolintlint 9 | - stylecheck 10 | - unparam 11 | - whitespace 12 | - revive 13 | - wsl 14 | - funlen 15 | - lll 16 | - misspell 17 | - nilerr 18 | - nlreturn 19 | 20 | linters-settings: 21 | funlen: 22 | lines: 260 #TODO Krasi: switch to 100 23 | statements: 88 #TODO Krasi: switch to 50 24 | wsl: 25 | allow-cuddle-declarations: true 26 | gocyclo: 27 | min-complexity: 67 #TODO Krasi: switch to 15 28 | goimports: 29 | local-prefixes: github.com/coretrix/hitrix 30 | gci: 31 | sections: 32 | - standard # Standard section: captures all standard packages. 33 | - default # Default section: contains all imports that could not be matched to another section type. 34 | - prefix(github.com/coretrix/hitrix) # Custom section: groups all imports with the specified Prefix. 35 | lll: 36 | line-length: 150 37 | misspell: 38 | locale: US 39 | nolintlint: 40 | require-explanation: true 41 | forbidigo: 42 | forbid: 43 | - ^fmt\.Print(|f|ln)$ 44 | - ^spew\.Dump$ 45 | 46 | issues: 47 | max-issues-per-linter: 0 48 | max-same-issues: 0 49 | 50 | run: 51 | timeout: 180s 52 | go: '1.17' -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export GO111MODULE=on 2 | 3 | format-check: ## Format the code and run linters 4 | @if test ! -e ./bin/golangci-lint; then \ 5 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./bin v1.54.1; \ 6 | fi 7 | @./bin/golangci-lint run --fix 8 | 9 | test-cover: ## Run tests with coverage 10 | @go install github.com/ory/go-acc@latest 11 | @go-acc ./... --output=coverage.out --covermode=atomic -- -race -p 1 12 | 13 | init: 14 | cd ./example && go run github.com/99designs/gqlgen init 15 | 16 | hitrix: 17 | ./example/docker/services.sh server 18 | 19 | single-instance-cron: 20 | ./example/docker/services.sh single-instance-cron 21 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # https://docs.codecov.io/docs/commit-status 2 | coverage: 3 | range: 0..50 4 | round: down 5 | precision: 0 6 | status: 7 | project: 8 | default: 9 | target: 14% 10 | threshold: 0% 11 | patch: 12 | default: 13 | target: 14% 14 | threshold: 0% -------------------------------------------------------------------------------- /datalayer/orm.go: -------------------------------------------------------------------------------- 1 | package datalayer 2 | 3 | import ( 4 | redisearch "github.com/coretrix/beeorm-redisearch-plugin" 5 | "github.com/latolukasz/beeorm/v2" 6 | ) 7 | 8 | type ORM struct { 9 | beeorm.Engine 10 | *redisearch.RedisSearchEngine 11 | } 12 | 13 | func (d *ORM) Clone() *ORM { 14 | return &ORM{ 15 | Engine: d.Engine.Clone(), 16 | RedisSearchEngine: d.RedisSearchEngine, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .temp 3 | .cache 4 | -------------------------------------------------------------------------------- /docs/docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | const config = require('./config/index') 2 | 3 | module.exports = { 4 | base: '/hitrix/', 5 | title: 'Hitrix - golang framework', 6 | description: 'Golang Framework for high traffic applications. Designed for speed up development time.', 7 | head: [ 8 | ['link', {rel: "shortcut icon", href: "logo-favicon.png"}], 9 | ['meta', {name: 'theme-color', content: '#D7A318'}], 10 | ['meta', {name: 'apple-mobile-web-app-capable', content: 'yes'}], 11 | ['meta', {name: 'apple-mobile-web-app-status-bar-style', content: 'black'}] 12 | ], 13 | themeConfig: { 14 | repo: 'https://github.com/coretrix/hitrix', 15 | docsRepo: 'https://github.com/coretrix/hitrix', 16 | logo: '/logo-favicon-90x90.png', 17 | editLinks: true, 18 | docsDir: 'docs/docs', 19 | editLinkText: '', 20 | lastUpdated: true, 21 | smoothScroll: true, 22 | algolia: config.Algolia, 23 | navbar: config.Navigation, 24 | sidebar: config.Sidebar, 25 | }, 26 | plugins: [ 27 | ['@vuepress/plugin-search', config.Search], 28 | ['@vuepress/plugin-back-to-top', true], 29 | ['@vuepress/plugin-medium-zoom', true], 30 | ['vuepress-plugin-sitemap', { hostname: 'https://coretrix.github.io/hitrix' }], 31 | // ['@vuepress/google-analytics', { 'ga': ''}] 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /docs/docs/.vuepress/config/algolia.js: -------------------------------------------------------------------------------- 1 | // Put Algolia config here 2 | module.exports = { 3 | apiKey: '21fc128e1fae00977c070d0f30e7b0cd', 4 | indexName: 'vuepress', 5 | appId: 'WMOLNCX6UH', 6 | algoliaOptions: { 7 | facetFilters: ['tags:v1'] 8 | } 9 | } -------------------------------------------------------------------------------- /docs/docs/.vuepress/config/index.js: -------------------------------------------------------------------------------- 1 | const Navigation = require('./nav') 2 | const Sidebar = require('./sidebar') 3 | const Algolia = require('./algolia') 4 | const Search = require('./search') 5 | 6 | module.exports = { 7 | Algolia, 8 | Navigation, 9 | Search, 10 | Sidebar 11 | } -------------------------------------------------------------------------------- /docs/docs/.vuepress/config/nav.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | text: 'Guide', 4 | link: '/', 5 | }, 6 | { 7 | text: 'Roadmap', 8 | link: '/roadmap/' 9 | }, 10 | { 11 | text: 'CoreTrix Rules', 12 | link: '/rules/' 13 | }, 14 | ] -------------------------------------------------------------------------------- /docs/docs/.vuepress/config/search.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | locales: { 3 | '/': { 4 | placeholder: 'Search...' 5 | } 6 | }, 7 | maxSuggestions: 10, 8 | getExtraFields: (page) => page.frontmatter.tags ?? [] 9 | } 10 | -------------------------------------------------------------------------------- /docs/docs/.vuepress/public/logo-favicon-90x90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coretrix/hitrix/3740ffa9352d5b402f4da0bb56ddca25b9aab8ae/docs/docs/.vuepress/public/logo-favicon-90x90.png -------------------------------------------------------------------------------- /docs/docs/.vuepress/public/logo-favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coretrix/hitrix/3740ffa9352d5b402f4da0bb56ddca25b9aab8ae/docs/docs/.vuepress/public/logo-favicon.png -------------------------------------------------------------------------------- /docs/docs/guide/features/consumer_runners.md: -------------------------------------------------------------------------------- 1 | # Consumer runners 2 | 3 | Consumer runners enable you to quickly spin up BeeORM queue consumers easily. 4 | There are 2 types of consumer. 5 | - scalable 6 | - non scalable 7 | 8 | 9 | ### ConsumerRunner - non scalable 10 | Use ```queue.NewConsumerRunner(ctx)``` to make consumers which are not required to be able 11 | to scale. 12 | 13 | This consumer works with following 4 interfaces: 14 | - ConsumerOne (consumes items one by one) 15 | - ConsumerMany (consumes items in batches) 16 | - ConsumerOneByModulo (consumes items one by one using modulo) 17 | - ConsumerManyByModulo (consumes items in batches using modulo) 18 | 19 | ### ScalableConsumerRunner - scalable 20 | Use ```queue.NewScalableConsumerRunner(ctx, persistent redis pool)``` to make consumers which are required to be able 21 | to scale. 22 | 23 | This consumer works with following 2 interfaces: 24 | - ConsumerOne (consumes items one by one) 25 | - ConsumerMany (consumes items in batches) 26 | -------------------------------------------------------------------------------- /docs/docs/guide/features/flags.md: -------------------------------------------------------------------------------- 1 | # Flags 2 | ## Pre deploy 3 | If you run your binary with argument `-pre-deploy` the program will check for alters and if there is no alters it will exit with code 0 but if there is an alters it will exit with code 1. 4 | 5 | ## Force alters 6 | If you run your binary with argument `-force-alters` the program will check for DB and RediSearch alters and it will execute them(only in local and qa mode). 7 | You can use this command on localhost if you use make file: 8 | `mç` 9 | 10 | You can use this feature during the deployment process check if you need to execute the alters before you deploy it 11 | -------------------------------------------------------------------------------- /docs/docs/guide/features/goroutine.md: -------------------------------------------------------------------------------- 1 | # Goroutine 2 | Go provides simple solution for concurrency using go keyword, but in any case that goroutine panics you have to handle that 3 | yourself. Hitrix provides `Goroutine` function that does this automatically for you and you don't have to be worry about 4 | your goroutine panic. Hitrix Goroutine function would recover the panic for you and log the error. 5 | You can use that like this: 6 | ```go 7 | package mypackage 8 | 9 | import "github.com/coretrix/hitrix" 10 | 11 | func Myfunc(){ 12 | hitrix.Goroutine(someFunc()) 13 | } 14 | ``` 15 | 16 | Hitrix also provides another function named `GoroutineWithRestart`. If you have a function that must be run all the time 17 | you can use this function. In any case of panics it would log the error and automatically start the function again. 18 | You can use this function like this: 19 | ```go 20 | package mypackage 21 | 22 | import "github.com/coretrix/hitrix" 23 | 24 | func Myfunc(){ 25 | hitrix.GoroutineWithRestart(someFunc()) 26 | } 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /docs/docs/guide/features/helper.md: -------------------------------------------------------------------------------- 1 | # Helpers 2 | We have different type of helpers that will provide you simple universal functions that will save your time. 3 | Please review folder `pkg/helpers`. We have helpers like: 4 | `array, auth, call, file, graphql, mysql, price, time, validator geo` and so on 5 | 6 | Only way to be aware with them is to spend some time check the code and inform yourself what can be helpful for you from our helpers -------------------------------------------------------------------------------- /docs/docs/guide/features/pagination.md: -------------------------------------------------------------------------------- 1 | # Pagination 2 | You can use: 3 | ```go 4 | package helper 5 | 6 | type URLQueryPager struct { 7 | // example = ?current_page=1&page_size=25 8 | CurrentPage int `binding:"min=1" form:"current_page"` 9 | PageSize int `binding:"min=1" form:"page_size"` 10 | } 11 | ``` 12 | in your code that needs pagination like: 13 | 14 | ```go 15 | package mypackage 16 | 17 | import "github.com/coretrix/hitrix/pkg/helper" 18 | 19 | type SomeURLQuery struct { 20 | helper.URLQueryPager 21 | OtherField1 string `form:"other_field_1"` 22 | OtherField2 int `form:"other_field_2"` 23 | } 24 | ``` -------------------------------------------------------------------------------- /docs/docs/guide/features/upload_files.md: -------------------------------------------------------------------------------- 1 | # Upload files 2 | 3 | Hitrix allow us to upload files using OSS service and assign them to FileEntity. 4 | Whenever we want to reassign them to the right entity we need to do something like that in separate endpoint 5 | ```go 6 | //... 7 | fileEntity := &entity.FileEntity{} 8 | found := ormService.LoadByID(fileID, fileEntity) 9 | 10 | if !found { 11 | return fmt.Errorf("file with FileID %v not found", *fileID) 12 | } 13 | 14 | if fileEntity.Namespace != oss.NamespaceUserAvatar.String() { 15 | return goErrors.New("wrong file category") 16 | } 17 | 18 | userEntity.Avatar = fileEntity.File 19 | //... 20 | ``` 21 | 22 | If you want to enable this feature you should call `middleware.FileRouter(ginEngine)` 23 | This will add `/v1/file/upload/` endpoint where the customers can upload their files 24 | -------------------------------------------------------------------------------- /docs/docs/guide/services/api_logger.md: -------------------------------------------------------------------------------- 1 | # API logger service 2 | This service is used to track every api request and response. 3 | For example it can be used in any other service as SMS service, Stripe service and so on. Using it you will have a history of all requests and responses 4 | and it will help you even in case you need to debug something. 5 | 6 | Register the service into your `main.go` file: 7 | ```go 8 | registry.APILogger(&entity.APILogEntity{}), 9 | ``` 10 | 11 | Access the service: 12 | ```go 13 | service.DI().APILogger() 14 | ``` 15 | 16 | All the data it will be saved into the `APILogEntity` entity. 17 | 18 | The methods that this service provide are: 19 | ```go 20 | type APILogger interface { 21 | LogStart(logType string, request interface{}) 22 | LogError(message string, response interface{}) 23 | LogSuccess(response interface{}) 24 | } 25 | ``` 26 | You should call `LogStart` before you send request to the api 27 | 28 | You should call `LogError` in case api return you error 29 | 30 | You should call `LogSuccess` in case api return you success 31 | -------------------------------------------------------------------------------- /docs/docs/guide/services/app.md: -------------------------------------------------------------------------------- 1 | # App 2 | This service provide information about the application like `MODE`, `NAME` and so on. 3 | 4 | It is auto registered so you don't need to register it 5 | 6 | Access the service: 7 | ```go 8 | service.DI().App() 9 | ``` -------------------------------------------------------------------------------- /docs/docs/guide/services/checkout.md: -------------------------------------------------------------------------------- 1 | # Checkout.com API 2 | 3 | This service is used to create a payment, check webhook key, request a refund and manage user cards for checkout.com payment API 4 | 5 | Register the service into your `main.go` file: 6 | 7 | ```go 8 | hitrixRegistry.ServiceProviderCheckout() 9 | ``` 10 | 11 | And you should put your credentials and other configs in `config/hitrix.yml` 12 | 13 | ```yml 14 | checkout: 15 | secret_key: secret 16 | public_key: public 17 | currency: USD 18 | webhook_keys: 19 | main: somekey 20 | ``` 21 | 22 | Access the service: 23 | ```go 24 | checkoutService := service.DI().Checkout() 25 | ``` 26 | 27 | 28 | Using the service: 29 | ```go 30 | // Request a payment 31 | checkoutService.RequestPayment( 32 | payments.IDSource{ 33 | Type: "id", 34 | ID: "sometoken", 35 | }, 36 | 100, 37 | "USD", 38 | "Order-1000", 39 | &payments.Customer{Email: "email@email.com"}, 40 | map[string]string{"OrderId": "Order-1000"} 41 | ) 42 | 43 | // Request a refund 44 | checkoutService.RequestRefunds(1000, "PaymentId", "Order-1000", map[string]string{"OrderId": "Order-1000", "RefundsID": "Order-1000"}) 45 | 46 | // Validating incoming webhook 47 | checkoutService.CheckWebhookKey("main", "value of Authorization header") 48 | 49 | // Get user cards 50 | checkoutService.GetCustomerInstruments("cus_someid") 51 | 52 | // Delete user card 53 | checkoutService.DeleteCustomerInstrument("src_someid") 54 | ``` 55 | -------------------------------------------------------------------------------- /docs/docs/guide/services/clock.md: -------------------------------------------------------------------------------- 1 | # Clock service 2 | This service is used for `time` operations. It is better to use it everywhere instead of `time.Now()` because it can be mocked and you can set whatever time you want in your tests 3 | 4 | Register the service into your `main.go` file: 5 | ```go 6 | registry.ServiceClock(), 7 | ``` 8 | 9 | Access the service: 10 | ```go 11 | service.DI().Clock() 12 | ``` 13 | 14 | The methods that this service provide are: 15 | ```Now() and NowPointer()``` 16 | -------------------------------------------------------------------------------- /docs/docs/guide/services/ddos.md: -------------------------------------------------------------------------------- 1 | # DDOS Protection 2 | This service contains DDOS protection features 3 | 4 | Register the service into your `main.go` file: 5 | ```go 6 | registry.ServiceProviderDDOS() 7 | ``` 8 | 9 | Access the service: 10 | ```go 11 | service.DI().DDOS() 12 | ``` 13 | 14 | You can protect for example login endpoint from many attempts by using method `ProtectManyAttempts` 15 | -------------------------------------------------------------------------------- /docs/docs/guide/services/error_logger.md: -------------------------------------------------------------------------------- 1 | # Error Logger 2 | Used to save unhandled errors in error log. Hitrix use `recovery` function to handle those errors. 3 | If you setup Slack service you also going to receive notifications in your slack 4 | 5 | Register the service into your `main.go` file: 6 | ```go 7 | registry.ServiceProviderErrorLogger() 8 | ``` 9 | 10 | Access the service: 11 | ```go 12 | service.DI().ErrorLogger() 13 | ``` 14 | 15 | It can be used to save custom errors as well: 16 | ```go 17 | errorLoggerService := ioc.GetErrorLoggerService() 18 | 19 | errorLoggerService.LogErrorWithRequest(c, err) //if you provide context we will save request body as well 20 | errorLoggerService.LogError(err) 21 | ``` -------------------------------------------------------------------------------- /docs/docs/guide/services/exporter.md: -------------------------------------------------------------------------------- 1 | # Exporter service 2 | This service is able to export business data to various file formats. 3 | Currently, we support 2 file formats: `XLSX` and `CSV`. 4 | 5 | Register the service into your `main.go` file: 6 | 7 | ```go 8 | registry.ServiceProviderExporter() 9 | ``` 10 | 11 | Access the service: 12 | 13 | ```go 14 | service.DI().Exporter() 15 | ``` 16 | 17 | Input data should be filled in as follows: 18 | 19 | ```go 20 | sheet := "sheet 1" 21 | headers := []string{"Header 1", "Header 2"} 22 | 23 | rows := make([][]interface{}, 0) 24 | 25 | var firstRow []interface{} 26 | firstRow = append(firstRow, "cell 1", "cell 2") 27 | rows = append(rows, firstRow) 28 | 29 | var secondRow []interface{} 30 | secondRow = append(secondRow, "cell 1", "cell 2") 31 | rows = append(rows, secondRow) 32 | ``` 33 | 34 | Use `XLSXExportToByte()` function to convert raw data to Excel file and return it as a byte slice: 35 | ```go 36 | xlsxBytes, err := exporterService.XLSXExportToByte(sheet, headers, rows) 37 | ``` 38 | 39 | Use `XLSXExportToFile()` function for converting raw data to Excel file and save it in the given path: 40 | ```go 41 | err := exporterService.XLSXExportToFile(sheet, headers, rows, filePath) 42 | ``` 43 | 44 | Use `CSVExportToByte()` function to convert raw data to CSV file and return it as a byte slice: 45 | ```go 46 | csvBytes, err := exporterService.CSVExportToByte(headers, rows) 47 | ``` 48 | 49 | Using `CSVExportToFile()` function for converting raw data to CSV file and save it in the given path: 50 | ```go 51 | err := exporterService.XLSXExportToFile(headers, rows, filePath) 52 | ``` -------------------------------------------------------------------------------- /docs/docs/guide/services/fcm.md: -------------------------------------------------------------------------------- 1 | # Firebase cloud messaging (FCM) service 2 | This service is used for sending different types of push notifications 3 | 4 | Register the service into your `main.go` file: 5 | ```go 6 | registry.ServiceProviderFCM(), 7 | ``` 8 | 9 | Config sample: 10 | 11 | expose `FIREBASE_CONFIG="path/to/service-account-file.json"` 12 | -------------------------------------------------------------------------------- /docs/docs/guide/services/file_extractor.md: -------------------------------------------------------------------------------- 1 | # File extractor service 2 | File extractor provides you a simple function to search in a path recursively and find terms based on a regular expression. 3 | 4 | Register the service into your `main.go` file: 5 | ```go 6 | registry.ServiceProviderExtractor(), 7 | ``` 8 | 9 | Access the service: 10 | ```go 11 | service.DI().FileExtractorService() 12 | ``` 13 | Extract phrase (errors in this example): 14 | ```go 15 | errorTerms, err := extractService.Extract(fileextractor.ExtractParams{ 16 | SearchPath: "./", 17 | Excludes: []string{}, 18 | Expression: `errors.New[(]*\("([^)]*)"\)`, 19 | }) 20 | if err != nil { 21 | // handle error 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/docs/guide/services/geocoding.md: -------------------------------------------------------------------------------- 1 | # Geocoding 2 | 3 | This service is used for geocoding and reverse geocoding. It supports multiple providers. For now only 4 | Google Maps provider is implemented. 5 | 6 | 7 | You should put your api key from Google Maps in config: 8 | 9 | ```yml 10 | geocoding: 11 | use_caching: true 12 | cache_ttl_min_days: 5 13 | cache_ttl_max_days: 10 14 | google_maps: 15 | api_key: some_key 16 | ``` 17 | 18 | Note 1: if you decide to use caching functionality, you need to run script `ClearExpiredGeocodingCache` 19 | in your project. This script will delete expired cache. 20 | 21 | Note 2: if you decide to use caching functionality, lat/lng are cut (not rounded) to 5 decimal place 22 | 23 | Access the service: 24 | ```go 25 | service.DI().Geocoding() 26 | ``` 27 | 28 | The service exposes 3 methods that you can use: 29 | 30 | ```go 31 | type IGeocoding interface { 32 | Geocode(ctx context.Context, ormService *datalayer.DataLayer, address string, language Language) (*Address, error) 33 | ReverseGeocode(ctx context.Context, ormService *datalayer.DataLayer, latLng *LatLng, language Language) (*Address, error) 34 | CutCoordinates(float float64, precision int) (float64, error) 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/docs/guide/services/google_analytics.md: -------------------------------------------------------------------------------- 1 | # Google Analytics 2 | 3 | This service is used for querying from Google Analytics 4 | 5 | Register the service into your `main.go`. You need to provide function that init the analytics type (UA or GA4) 6 | 7 | ```go 8 | registry.ServiceProviderGoogleAnalytics(googleanalytics.NewGA4) 9 | ``` 10 | 11 | Then you need to download client configuration (credentials file) from your panel and put it in configs folder. 12 | After that, you should put your ID of your GA property and config file name in config yaml file 13 | 14 | Example: 15 | ```yml 16 | google_analytics: 17 | config_file_name: Name-s0m3r4nd0mv4lu3.json 18 | property_id: 123456789 19 | ``` 20 | 21 | 22 | Access the service: 23 | ```go 24 | service.DI().GoogleAnalytics().GetProvider(googleanalytics.GA4) 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/docs/guide/services/gql.md: -------------------------------------------------------------------------------- 1 | # Gql 2 | This service can be used to return GQL errors and translate them if localize service is registered 3 | 4 | Register the service into your `main.go` file: 5 | ```go 6 | registry.ServiceProviderGql() 7 | ``` 8 | 9 | Access the service: 10 | ```go 11 | service.DI().Gql() 12 | ``` 13 | 14 | The methods that can be used are `GraphqlErr` and `GraphqlErrPath` -------------------------------------------------------------------------------- /docs/docs/guide/services/html2pdf.md: -------------------------------------------------------------------------------- 1 | # HTML2PDF service 2 | HTML2PDF service provides a generating pdf function from html code using Chrome headless. 3 | 4 | First you need these in your app config: 5 | ```yaml 6 | chrome_headless: 7 | web_socket_url: ENV[CHROME_HEADLESS_WEB_SOCKET_URL] 8 | ``` 9 | Register the service into your `main.go` file: 10 | 11 | ```go 12 | registry.ServiceProviderHTML2PDF() 13 | ``` 14 | 15 | Access the service: 16 | ```go 17 | service.DI().HTML2PDFService() 18 | ``` 19 | Using `HtmlToPdf()` function to generate PDF from html: 20 | ```go 21 | pdfBytes := html2pdfService.HtmlToPdf("

Hi!

") 22 | ``` 23 | 24 | Recommended docker file for Chrome headless: 25 | ``` 26 | https://hub.docker.com/r/chromedp/headless-shell/ 27 | ``` -------------------------------------------------------------------------------- /docs/docs/guide/services/jwt.md: -------------------------------------------------------------------------------- 1 | # JWT 2 | You can use that service to encode and decode JWT tokens 3 | 4 | Register the service into your `main.go` file: 5 | ```go 6 | registry.ServiceProviderJWT() 7 | ``` 8 | 9 | Access the service: 10 | ```go 11 | service.DI().JWT() 12 | ``` -------------------------------------------------------------------------------- /docs/docs/guide/services/kubernetes.md: -------------------------------------------------------------------------------- 1 | # Kubernetes 2 | 3 | This service is used for calling Kubernetes APIs. 4 | Currently, this service can be used to add other domains to Kubernetes Ingresses and handling 5 | certification generation with cert-manager. 6 | 7 | To use this, register the service into your `main.go` file first: 8 | 9 | ```go 10 | registry.ServiceProviderKubernetes() 11 | ``` 12 | 13 | and you should put your credentials and other configs in your config file 14 | 15 | ```yml 16 | kubernetes: 17 | environment: "dev" 18 | config_file: "/path/to/kubeconfig" 19 | ``` 20 | the `config_file` can be one of these: 21 | - absolute path to config file 22 | - relative path to config file - then address of config directory will be prepended to it 23 | - can be omitted completely - then Kubernetes in-cluster config of [Service Account Token](https://kubernetes.io/docs/admin/authentication/#service-account-tokens) will be used 24 | 25 | Access the service: 26 | 27 | ```go 28 | service.DI().Kubernetes() 29 | ``` 30 | 31 | Some functions this service provide are: 32 | 33 | ```go 34 | GetIngressDomains(ctx context.Context) ([]string, error) 35 | AddIngress(ctx context.Context, domain, secretName, serviceName, servicePortName string, annotations map[string]string) error 36 | RemoveIngress(ctx context.Context, domain string) error 37 | IsCertificateProvisioned(ctx context.Context, secretName string) (bool, error) 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/docs/guide/services/license_plate_recognizer.md: -------------------------------------------------------------------------------- 1 | # Licence plate recognizer 2 | This service allow us to upload base64, and it will return all car license plates on this image. 3 | 4 | You should put your api key from `platerecognizer.com` in config: 5 | 6 | ```yml 7 | platerecognizer: 8 | api_key: some_key 9 | ``` 10 | 11 | Access the service: 12 | ```go 13 | service.DI().LicencePlateRecognizer() 14 | ``` 15 | -------------------------------------------------------------------------------- /docs/docs/guide/services/orm_engine.md: -------------------------------------------------------------------------------- 1 | # ORM Engine 2 | Used to access ORM in background scripts. It is one instance for the whole script 3 | 4 | Register the service into your `main.go` file: 5 | ```go 6 | registry.ServiceProviderOrmEngine() 7 | ``` 8 | 9 | Access the service: 10 | ```go 11 | service.DI().ORMEngine() 12 | ``` 13 | 14 | Never use that service in API. It is not thread safe! -------------------------------------------------------------------------------- /docs/docs/guide/services/orm_engine_context.md: -------------------------------------------------------------------------------- 1 | # ORM Engine Context 2 | Used to access ORM in foreground scripts like API. It is one instance per every request 3 | 4 | Register the service into your `main.go` file as context service: 5 | ```go 6 | registry.ServiceProviderOrmEngineForContext() 7 | ``` 8 | 9 | Access the service: 10 | ```go 11 | service.DI().ORMEngineForContext() 12 | ``` -------------------------------------------------------------------------------- /docs/docs/guide/services/password.md: -------------------------------------------------------------------------------- 1 | # Password 2 | This service it can be used to hash and verify hashed passwords. 3 | 4 | Register the service into your `main.go` file: 5 | ```go 6 | registry.ServiceProviderPassword(password.NewSimpleManager) 7 | ``` 8 | 9 | Access the service: 10 | ```go 11 | service.DI().Password() 12 | ``` -------------------------------------------------------------------------------- /docs/docs/guide/services/sentry.md: -------------------------------------------------------------------------------- 1 | # Sentry service 2 | This service allow you to use sentry for logging events and performance tracking. 3 | 4 | Register the service into your `main.go` file: 5 | ```go 6 | registry.ServiceProviderSentry(tracesSampleRate *float64) 7 | ``` 8 | 9 | Access the service: 10 | ```go 11 | service.DI().Sentry() 12 | ``` 13 | 14 | The methods that this service provide are: 15 | ```go 16 | type ISentry interface { 17 | CaptureMessage(message string) 18 | Flush(timeout time.Duration) 19 | StartSpan(ctx context.Context, operation string, options ...sentry.SpanOption) *sentry.Span 20 | } 21 | ``` 22 | You should call `CaptureMessage` when you want to send event to sentry 23 | 24 | You should call `Flush` in your main file with defer, with 2 second timeout 25 | 26 | You should call `StartSpan` when you want to start performance monitor 27 | -------------------------------------------------------------------------------- /docs/docs/guide/services/setting.md: -------------------------------------------------------------------------------- 1 | # Setting service 2 | If your application requires configurations that might change or predefined, you need to use setting service. You should save your settings in `SettingsEntity`, then use this service to fetch it. 3 | 4 | 5 | Register the service into your `main.go` file: 6 | ```go 7 | registry.ServiceProviderSetting() 8 | ``` 9 | 10 | Access the service: 11 | ```go 12 | service.DI().Setting() 13 | ``` 14 | 15 | # Use case 16 | 17 | Imagine you need to restrict access to login page after certain number of failed login attempts. You can simply store this value in `SettingsEntity` and fetch it using this service: 18 | ```go 19 | package save 20 | import ( 21 | "service" 22 | ) 23 | 24 | func SaveConfig(){ 25 | ormService := service.DI().ORMEngine() 26 | ormService.Flush(&entity.SettingsEntity{ 27 | Key: "user.login.threshold", 28 | Value: "3", 29 | ValueType: entity.SettingsValueTypeAll.SettingsValueTypeNumber, 30 | }) 31 | } 32 | ``` 33 | 34 | Then later in your login package, you can retrieve this value and use it: 35 | 36 | ```go 37 | package login 38 | import ( 39 | "errors" 40 | "service" 41 | ) 42 | 43 | func Login(currentCount uint64) error { 44 | ormService := service.DI().ORMEngine() 45 | allowed, found := service.DI().Setting().GetUint64(ormService, "user.login.threshold") 46 | if found && currentCount> allowed{ 47 | return errors.New("too many login attempt") 48 | } 49 | return nil 50 | } 51 | ``` -------------------------------------------------------------------------------- /docs/docs/guide/services/slack.md: -------------------------------------------------------------------------------- 1 | # Slack 2 | Gives you ability to send Slack messages using slack bot. You can define multiple Slack bots. 3 | Also, it's used to send messages if you use our `ErrorLogger` service using `errors` bot. 4 | The config that needs to be set in hitrix.yaml is: 5 | 6 | ```yaml 7 | slack: 8 | error_channel: "test" #optional, used by ErrorLogger 9 | dev_panel_url: "test" #optional, used by ErrorLogger 10 | bot_tokens: 11 | errors: "your token" 12 | another_bot: "second token" 13 | ``` 14 | 15 | > NOTE: `bot_tokens.errors` is a must-have when using `ErrorLogger` service. 16 | 17 | Register the service into your `main.go` file: 18 | ```go 19 | registry.ServiceProviderSlack() 20 | ``` 21 | 22 | Access the service: 23 | ```go 24 | service.DI().Slack() 25 | ``` -------------------------------------------------------------------------------- /docs/docs/guide/services/stripe.md: -------------------------------------------------------------------------------- 1 | # Stripe 2 | 3 | Stripe payment integration 4 | 5 | Register the service into your `main.go` file: 6 | ```go 7 | registry.ServiceProviderStripe(), 8 | ``` 9 | 10 | Access the service: 11 | ```go 12 | service.DI().Stripe() 13 | ``` 14 | 15 | Config sample: 16 | 17 | ```yml 18 | stripe: 19 | key: "api_key" 20 | webhook_secrets: # map of your webhook secrets 21 | checkout: "key" 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/docs/guide/services/template.md: -------------------------------------------------------------------------------- 1 | # Template 2 | This service can be used to render templates from your html files and also from mandrill templates 3 | 4 | Register the service into your `main.go` file: 5 | ```go 6 | registry.ServiceProviderTemplate() 7 | ``` 8 | 9 | Access the service: 10 | ```go 11 | service.DI().Template() 12 | ``` -------------------------------------------------------------------------------- /docs/docs/guide/services/uploader.md: -------------------------------------------------------------------------------- 1 | # Uploader 2 | 3 | This service uses TUS protocol to enable fast resumable and multi-part upload of big files. 4 | It provides an easy interface for plug-in whatever data store and locker you want to implement. 5 | Currently, Amazon S3 data store and Redis locker are implemented. For Amazon data store to work, 6 | you need to register Amazon S3 service before this one, also for Redis locker to work, you need 7 | to register orm service background before this one. 8 | 9 | Register the service into your `main.go` file: 10 | ```go 11 | registry.ServiceProviderUploader(tusd.Config{...}, datastore.GetAmazonS3Store, locker.GetRedisLocker) 12 | ``` 13 | 14 | Access the service: 15 | ```go 16 | service.DI().Uploader() 17 | ``` 18 | 19 | Hitrix also provides REST uploader controller which you can register all handler methods in your 20 | router: 21 | 22 | ```go 23 | var uploaderController *hitrixController.UploaderController 24 | uploaderGroup := ginEngine.Group("/files/") 25 | uploaderGroup.Use(middleware.AuthorizeWithHeaderStrict()) 26 | { 27 | uploaderGroup.POST("", uploaderController.PostFileAction) 28 | uploaderGroup.HEAD(":id", uploaderController.HeadFile) 29 | uploaderGroup.PATCH(":id", uploaderController.PatchFile) 30 | uploaderGroup.GET(":id", uploaderController.GetFileAction) 31 | uploaderGroup.DELETE(":id", uploaderController.DeleteFile) 32 | } 33 | ``` 34 | 35 | Also you need bucket name in config: 36 | 37 | ````yml 38 | uploader: 39 | bucket: media 40 | ```` -------------------------------------------------------------------------------- /docs/docs/roadmap/README.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | ## Planned in next release 4 | 5 | * Refactor Authentication service 6 | * Fix JWT token issue 7 | 8 | ## Planned in near feature 9 | 10 | * Add access log to dev-panel using kubernetes api -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "docs:dev": "vuepress dev docs", 4 | "docs:build": "vuepress build docs" 5 | }, 6 | "dependencies": { 7 | "@vuepress/plugin-search": "2.0.0-beta.66", 8 | "vuepress": "2.0.0-beta.66", 9 | "vuepress-plugin-sitemap": "^2.3.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /example/cmd/single-instance-cron/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/coretrix/hitrix" 5 | "github.com/coretrix/hitrix/example/entity/initialize" 6 | "github.com/coretrix/hitrix/example/redis" 7 | "github.com/coretrix/hitrix/example/scripts" 8 | scriptsHitrix "github.com/coretrix/hitrix/scripts" 9 | "github.com/coretrix/hitrix/service/component/app" 10 | "github.com/coretrix/hitrix/service/registry" 11 | ) 12 | 13 | func main() { 14 | s, deferFunc := hitrix.New( 15 | "single-instance-cron", "secret", 16 | ).RegisterDIGlobalService( 17 | registry.ServiceProviderErrorLogger(), 18 | registry.ServiceProviderConfigDirectory("../../config"), 19 | registry.ServiceProviderOrmRegistry(initialize.Init), 20 | registry.ServiceProviderOrmEngine(redis.SearchPool), 21 | registry.ServiceProviderClock(), 22 | ).RegisterDIRequestService( 23 | registry.ServiceProviderOrmEngineForContext(false, redis.SearchPool), 24 | ).RegisterRedisPools(&app.RedisPools{ 25 | Cache: redis.DefaultPool, 26 | Persistent: redis.DefaultPool, 27 | Stream: redis.StreamsPool, 28 | Search: redis.SearchPool, 29 | }).Build() 30 | defer deferFunc() 31 | 32 | b := &hitrix.BackgroundProcessor{Server: s} 33 | b.RunAsyncOrmConsumer() 34 | b.RunAsyncRequestLoggerCleaner() 35 | 36 | s.RunBackgroundProcess(func(b *hitrix.BackgroundProcessor) { 37 | go b.RunScript(&scripts.DirtyConsumerScript{}) 38 | go b.RunScript(&scriptsHitrix.ReindexConsumerScript{}) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /example/config/.env.local: -------------------------------------------------------------------------------- 1 | DEFAULT_MYSQL=root:root@tcp(mysql:3306)/hitrix?multiStatements=true 2 | DEFAULT_MYSQL_LOG=root:root@tcp(mysql:3306)/hitrix_log?multiStatements=true 3 | DEFAULT_REDIS=redis:6379:1 4 | DEFAULT_REDIS_STREAMS=redis:6379:2 5 | DEFAULT_REDIS_SEARCH=redis:6379:0 6 | -------------------------------------------------------------------------------- /example/config/.env.local.sample: -------------------------------------------------------------------------------- 1 | DEFAULT_MYSQL=root:root@tcp(0.0.0.0:9004)/hitrix?multiStatements=true 2 | DEFAULT_MYSQL_LOG=root:root@tcp(0.0.0.0:9004)/hitrix_log?multiStatements=true 3 | DEFAULT_REDIS=redis:9002:1 4 | DEFAULT_REDIS_SEARCH=redis:9002:0 5 | 6 | -------------------------------------------------------------------------------- /example/config/.env.test: -------------------------------------------------------------------------------- 1 | DEFAULT_MYSQL=root:root@tcp(0.0.0.0:9004)/hitrix?multiStatements=true 2 | DEFAULT_MYSQL_LOG=root:root@tcp(0.0.0.0:9004)/hitrix_log?multiStatements=true 3 | DEFAULT_REDIS=0.0.0.0:9002:1 4 | DEFAULT_REDIS_STREAMS=0.0.0.0:9002:2 5 | DEFAULT_REDIS_SEARCH=0.0.0.0:9002:0 6 | -------------------------------------------------------------------------------- /example/config/.env.test.sample: -------------------------------------------------------------------------------- 1 | DEFAULT_MYSQL=root:root@tcp(0.0.0.0:9004)/hitrix?multiStatements=true 2 | DEFAULT_MYSQL_LOG=root:root@tcp(0.0.0.0:9004)/hitrix_log?multiStatements=true 3 | DEFAULT_REDIS=redis:9002:1 4 | DEFAULT_REDIS_SEARCH=redis:9002:0 5 | -------------------------------------------------------------------------------- /example/config/hitrix.yaml: -------------------------------------------------------------------------------- 1 | slack: 2 | error_channel: "" 3 | dev_panel_url: "" 4 | bot_tokens: 5 | errors: "your token" 6 | 7 | stripe: 8 | key: "api_key" 9 | webhook_secrets: 10 | checkout: "key" 11 | 12 | checkout: 13 | secret_key: "secret_key" 14 | public_key: "public_key" 15 | currency: "GBP" 16 | webhook_keys: 17 | main: "some_key" 18 | 19 | oss: 20 | amazon: 21 | endpoint: "https://somestorage.com" 22 | access_key_id: "" 23 | secret_access_key: "" 24 | disable_ssl: false 25 | region: us-east-1 26 | buckets: 27 | public: 28 | name: bucket-name 29 | cdn_url: "https://example.com/{Bucket}/{StorageKey}" 30 | ACL: public-read 31 | private: 32 | name: bucket-name-private 33 | cdn_url: "http://127.0.0.1/{Bucket}/{StorageKey}" 34 | 35 | server: 36 | timeout_sec: 10 37 | 38 | orm_debug: false 39 | 40 | google_calendar: 41 | credential_file: "./credentials.json" 42 | scopes: 43 | - "https://www.googleapis.com/auth/calendar" 44 | -------------------------------------------------------------------------------- /example/config/server/config.yaml: -------------------------------------------------------------------------------- 1 | orm: 2 | default: 3 | mysql: 4 | uri: ENV[DEFAULT_MYSQL] 5 | redis: ENV[DEFAULT_REDIS] 6 | locker: default 7 | local_cache: 1000 8 | log_db_pool: 9 | mysql: 10 | uri: ENV[DEFAULT_MYSQL_LOG] 11 | streams_pool: 12 | redis: ENV[DEFAULT_REDIS_STREAMS] 13 | search_pool: 14 | redis: ENV[DEFAULT_REDIS_SEARCH] 15 | authentication: 16 | secret: "a-deep-dark-secret" 17 | support_otp: true 18 | otp_length: 5 19 | cors: 20 | - http://localhost:9001 # This is the default port of https://github.com/coretrix/dev-frontend repository 21 | - http://localhost:63342 # websocket test 22 | -------------------------------------------------------------------------------- /example/config/single-instance-cron/config.yaml: -------------------------------------------------------------------------------- 1 | orm: 2 | default: 3 | mysql: 4 | uri: ENV[DEFAULT_MYSQL] 5 | redis: ENV[DEFAULT_REDIS] 6 | locker: default 7 | local_cache: 1000 8 | log_db_pool: 9 | mysql: 10 | uri: ENV[DEFAULT_MYSQL_LOG] 11 | streams_pool: 12 | redis: ENV[DEFAULT_REDIS_STREAMS] 13 | search_pool: 14 | redis: ENV[DEFAULT_REDIS_SEARCH] 15 | authentication: 16 | secret: "a-deep-dark-secret" 17 | support_otp: true 18 | otp_length: 5 19 | cors: 20 | - http://localhost:9001 # This is the default port of https://github.com/coretrix/dev-frontend repository 21 | - http://localhost:63342 # websocket test 22 | -------------------------------------------------------------------------------- /example/docker/.env: -------------------------------------------------------------------------------- 1 | COMPOSE_PROJECT_NAME=hitrix 2 | LOCAL_IP=0.0.0.0 3 | MYSQL_PORT=9004 4 | REDIS_PORT=9002 5 | DEFAULT_REDIS_SEARCH=9002 6 | -------------------------------------------------------------------------------- /example/docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rm -f .env 4 | 5 | echo COMPOSE_PROJECT_NAME=hitrix >> .env 6 | echo LOCAL_IP=0.0.0.0 >> .env 7 | echo MYSQL_PORT=9004 >> .env 8 | echo REDIS_PORT=9002 >> .env 9 | echo DEFAULT_REDIS_SEARCH=9002 >> .env 10 | 11 | docker-compose up -d --build -------------------------------------------------------------------------------- /example/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | mysql: 4 | image: mysql:8.0 5 | volumes: 6 | - orm_data_mysql_hitrix:/var/lib/mysql 7 | ports: 8 | - "9004:3306" 9 | environment: 10 | - MYSQL_ROOT_PASSWORD=root 11 | - MYSQL_DATABASE=hitrix 12 | - MYSQL_PASSWORD=root 13 | 14 | redis: 15 | image: redis/redis-stack 16 | volumes: 17 | - orm_data_redis_hitrix:/data 18 | ports: 19 | - "9002:6379" 20 | - "8001:8001" 21 | 22 | redisinsight: 23 | image: redislabs/redisinsight:latest 24 | ports: 25 | - "8002:8001" 26 | volumes: 27 | - redisinsight_hitrix:/var/lib/redisinsight 28 | 29 | services: 30 | build: 31 | context: "" 32 | dockerfile: services/Dockerfile 33 | depends_on: 34 | - mysql 35 | - redis 36 | ports: 37 | - "9999:9999" 38 | volumes: 39 | - ../..:/go/src/github.com/coretrix/hitrix 40 | 41 | volumes: 42 | orm_data_mysql_hitrix: 43 | orm_data_redis_hitrix: 44 | redisinsight_hitrix: 45 | -------------------------------------------------------------------------------- /example/docker/services.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd example/docker && docker-compose exec services /bin/sh -c "cd example/cmd/$1 && APP_MODE=local go run main.go" 4 | -------------------------------------------------------------------------------- /example/docker/services/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.19-alpine 2 | 3 | RUN mkdir -p /go/src/github.com/coretrix/hitrix 4 | 5 | WORKDIR /go/src/github.com/coretrix/hitrix 6 | 7 | ADD services/docker-entrypoint.sh /usr/bin/docker-entrypoint 8 | RUN chmod +x /usr/bin/docker-entrypoint 9 | 10 | ENTRYPOINT ["docker-entrypoint"] 11 | -------------------------------------------------------------------------------- /example/docker/services/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | tail -f /dev/null 4 | -------------------------------------------------------------------------------- /example/entity/admin_user_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/latolukasz/beeorm/v2" 5 | 6 | hitrixEntity "github.com/coretrix/hitrix/pkg/entity" 7 | ) 8 | 9 | type AdminUserEntity struct { 10 | beeorm.ORM `orm:"table=admin_users;log=log_db_pool;redisCache;redisSearch=search_pool"` 11 | ID uint64 12 | RoleID *hitrixEntity.RoleEntity `orm:"required"` 13 | } 14 | 15 | func (u *AdminUserEntity) SetRole(roleEntity *hitrixEntity.RoleEntity) { 16 | u.RoleID = roleEntity 17 | } 18 | -------------------------------------------------------------------------------- /example/entity/dev_panel_user_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/latolukasz/beeorm/v2" 4 | 5 | type DevPanelUserEntity struct { 6 | beeorm.ORM `orm:"crud-stream;table=dev_panel_users;redisCache;redisSearch=search_pool"` 7 | ID uint64 8 | Email string `orm:"unique=Email;searchable"` 9 | Password string `orm:"searchable"` 10 | FakeDelete bool 11 | } 12 | 13 | func (u *DevPanelUserEntity) GetUniqueFieldName() string { 14 | return "Email" 15 | } 16 | 17 | func (u *DevPanelUserEntity) GetUsername() string { 18 | return u.Email 19 | } 20 | 21 | func (u *DevPanelUserEntity) GetPassword() string { 22 | return u.Password 23 | } 24 | 25 | func (u *DevPanelUserEntity) CanAuthenticate() bool { 26 | return true 27 | } 28 | -------------------------------------------------------------------------------- /example/graph/model/models_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package model 4 | 5 | type NewTodo struct { 6 | Text string `json:"text"` 7 | UserID string `json:"userId"` 8 | } 9 | 10 | type Todo struct { 11 | ID string `json:"id"` 12 | Text string `json:"text"` 13 | Done bool `json:"done"` 14 | User *User `json:"user"` 15 | } 16 | 17 | type User struct { 18 | ID string `json:"id"` 19 | Name string `json:"name"` 20 | } 21 | -------------------------------------------------------------------------------- /example/graph/resolver.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | // This file will not be regenerated automatically. 4 | // 5 | // It serves as dependency injection for your app, add any dependencies you require here. 6 | 7 | type Resolver struct{} 8 | -------------------------------------------------------------------------------- /example/graph/schema.graphqls: -------------------------------------------------------------------------------- 1 | # GraphQL schema example 2 | # 3 | # https://gqlgen.com/getting-started/ 4 | 5 | type Todo { 6 | id: ID! 7 | text: String! 8 | done: Boolean! 9 | user: User! 10 | } 11 | 12 | type User { 13 | id: ID! 14 | name: String! 15 | } 16 | 17 | type Query { 18 | todos: [Todo!]! 19 | } 20 | 21 | input NewTodo { 22 | text: String! 23 | userId: String! 24 | } 25 | 26 | type Mutation { 27 | createTodo(input: NewTodo!): Todo! 28 | } -------------------------------------------------------------------------------- /example/graph/schema.resolvers.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | // This file will be automatically regenerated based on the schema, any resolver implementations 4 | // will be copied through when generating and any unknown code will be moved to the end. 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | 10 | "github.com/coretrix/hitrix/example/graph/generated" 11 | "github.com/coretrix/hitrix/example/graph/model" 12 | ) 13 | 14 | func (r *mutationResolver) CreateTodo(_ context.Context, _ model.NewTodo) (*model.Todo, error) { 15 | panic(fmt.Errorf("not implemented")) 16 | } 17 | 18 | func (r *queryResolver) Todos(_ context.Context) ([]*model.Todo, error) { 19 | panic(fmt.Errorf("not implemented")) 20 | } 21 | 22 | // Mutation returns generated.MutationResolver implementation. 23 | func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} } 24 | 25 | // Query returns generated.QueryResolver implementation. 26 | func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } 27 | 28 | type mutationResolver struct{ *Resolver } 29 | type queryResolver struct{ *Resolver } 30 | -------------------------------------------------------------------------------- /example/model/socket/registry.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/coretrix/hitrix/service/component/socket" 7 | ) 8 | 9 | const ( 10 | DefaultWebsocketNamespace = "default" 11 | ) 12 | 13 | func RegisterSocketHandler(_ *socket.Socket) { 14 | log.Println("Register Socket") 15 | } 16 | 17 | func UnRegisterSocketHandler(_ *socket.Socket) { 18 | log.Println("UnRegister Socket") 19 | } 20 | -------------------------------------------------------------------------------- /example/oss/oss.go: -------------------------------------------------------------------------------- 1 | package oss 2 | 3 | import ( 4 | "github.com/coretrix/hitrix/service/component/oss" 5 | ) 6 | 7 | const ( 8 | NamespaceAvatars oss.Namespace = "avatars" 9 | NamespaceInvoices oss.Namespace = "invoices" 10 | ) 11 | 12 | var Namespaces = oss.Namespaces{ 13 | NamespaceAvatars: oss.BucketPublic, 14 | NamespaceInvoices: oss.BucketPrivate, 15 | } 16 | -------------------------------------------------------------------------------- /example/redis/const.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | const ( 4 | DefaultPool = "default" 5 | SearchPool = "search_pool" 6 | StreamsPool = "streams_pool" 7 | ) 8 | -------------------------------------------------------------------------------- /example/rest/middleware/router.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | "github.com/coretrix/hitrix/example/rest/controller" 7 | "github.com/coretrix/hitrix/pkg/middleware" 8 | ) 9 | 10 | func Router(ginEngine *gin.Engine) { 11 | var websocketController *controller.WebsocketController 12 | ginEngine.GET("/ws/", websocketController.InitConnection) 13 | 14 | middleware.ACLRouter(ginEngine) 15 | } 16 | -------------------------------------------------------------------------------- /example/scripts/dirty_consumer_script.go: -------------------------------------------------------------------------------- 1 | package scripts 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/coretrix/hitrix/example/redis_stream/consumers" 7 | "github.com/coretrix/hitrix/pkg/queue" 8 | "github.com/coretrix/hitrix/service/component/app" 9 | ) 10 | 11 | type DirtyConsumerScript struct { 12 | } 13 | 14 | func (script *DirtyConsumerScript) Run(ctx context.Context, _ app.IExit) { 15 | queue.NewConsumerRunner(ctx).RunConsumerMany(consumers.NewDirtyConsumer(), nil, 1000) 16 | } 17 | 18 | func (script *DirtyConsumerScript) Infinity() bool { 19 | return true 20 | } 21 | 22 | func (script *DirtyConsumerScript) Unique() bool { 23 | return true 24 | } 25 | 26 | func (script *DirtyConsumerScript) Description() string { 27 | return "dirty consumer script" 28 | } 29 | -------------------------------------------------------------------------------- /goroutine.go: -------------------------------------------------------------------------------- 1 | package hitrix 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func Goroutine(fn func()) { 10 | go routine(fn, false) 11 | } 12 | 13 | func GoroutineWithRestart(fn func()) { 14 | go routine(fn, true) 15 | } 16 | 17 | func routine(fn func(), autoRestart bool) { 18 | defer func() { 19 | if r := recover(); r != nil { 20 | service.DI().ErrorLogger().LogError(r) 21 | 22 | if autoRestart { 23 | time.Sleep(time.Second) 24 | 25 | go routine(fn, true) 26 | } 27 | } 28 | }() 29 | 30 | fn() 31 | } 32 | -------------------------------------------------------------------------------- /pkg/binding/json.go: -------------------------------------------------------------------------------- 1 | package binding 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/gin-gonic/gin/binding" 8 | 9 | "github.com/coretrix/hitrix/pkg/errors" 10 | ) 11 | 12 | func ShouldBindJSON(c *gin.Context, form interface{}) error { 13 | if c.Request.Body == nil { 14 | return fmt.Errorf("body cannot be nil") 15 | } 16 | 17 | if err := c.ShouldBindBodyWith(form, binding.JSON); err != nil { 18 | res := errors.HandleErrors(err) 19 | if res != nil { 20 | return res 21 | } 22 | 23 | return err 24 | } 25 | 26 | return nil 27 | } 28 | 29 | func ShouldBindQuery(c *gin.Context, form interface{}) error { 30 | if err := c.ShouldBindQuery(form); err != nil { 31 | res := errors.HandleErrors(err) 32 | if res != nil { 33 | return res 34 | } 35 | 36 | return err 37 | } 38 | 39 | return nil 40 | } 41 | 42 | func ShouldBindURI(c *gin.Context, form interface{}) error { 43 | if err := c.ShouldBindUri(form); err != nil { 44 | res := errors.HandleErrors(err) 45 | if res != nil { 46 | return res 47 | } 48 | 49 | return err 50 | } 51 | 52 | return nil 53 | } 54 | 55 | func ShouldBind(c *gin.Context, form interface{}) error { 56 | if err := c.ShouldBind(form); err != nil { 57 | res := errors.HandleErrors(err) 58 | if res != nil { 59 | return res 60 | } 61 | 62 | return err 63 | } 64 | 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /pkg/controller/clockwork_controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | 8 | "github.com/coretrix/hitrix/pkg/response" 9 | "github.com/coretrix/hitrix/service" 10 | ) 11 | 12 | type ClockworkController struct { 13 | } 14 | 15 | func (controller *ClockworkController) GetIndexAction(c *gin.Context) { 16 | id := c.Param("id") 17 | if id == "" { 18 | response.NotFoundResponse(c) 19 | 20 | return 21 | } 22 | 23 | c.JSON(http.StatusOK, service.DI().ClockWorkForContext(c.Request.Context()).GetSavedData(id)) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/controller/file_controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | "github.com/coretrix/hitrix/pkg/binding" 7 | "github.com/coretrix/hitrix/pkg/dto/file" 8 | errorhandling "github.com/coretrix/hitrix/pkg/error_handling" 9 | fileModel "github.com/coretrix/hitrix/pkg/model/file" 10 | "github.com/coretrix/hitrix/pkg/response" 11 | ) 12 | 13 | type FileController struct { 14 | } 15 | 16 | // @Description Upload 17 | // @Tags File 18 | // @Accept mpfd 19 | // @Param body formData file.RequestDTOUploadFile true "Request in formData" 20 | // @Param file formData file true "The file" 21 | // @Router /file/upload/ [post] 22 | // @Security BearerAuth 23 | // @Success 200 {object} file.File 24 | // @Failure 400 {object} response.Error 25 | // @Failure 401 "Unauthorized" 26 | // @Failure 403 "Forbidden" 27 | // @Failure 500 "Something bad happened" 28 | func (controller *FileController) PostUploadImageAction(c *gin.Context) { 29 | req := &file.RequestDTOUploadFile{} 30 | 31 | err := binding.ShouldBind(c, req) 32 | 33 | if errorhandling.HandleError(c, err) { 34 | return 35 | } 36 | 37 | request, closeFile, err := req.ToUploadImage() 38 | 39 | if errorhandling.HandleError(c, err) { 40 | return 41 | } 42 | 43 | defer func() { 44 | _ = closeFile() 45 | }() 46 | 47 | res, err := fileModel.CreateFile(c.Request.Context(), request) 48 | 49 | if errorhandling.HandleError(c, err) { 50 | return 51 | } 52 | 53 | response.SuccessResponse(c, res) 54 | } 55 | -------------------------------------------------------------------------------- /pkg/controller/readiness_controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/latolukasz/beeorm/v2" 8 | 9 | "github.com/coretrix/hitrix/pkg/helper" 10 | "github.com/coretrix/hitrix/service" 11 | ) 12 | 13 | type ReadinessController struct { 14 | } 15 | 16 | func (controller *ReadinessController) GetReadinessAction(c *gin.Context) { 17 | ormService := service.DI().OrmEngine() 18 | 19 | var res int8 20 | 21 | has := ormService.GetMysql().QueryRow(beeorm.NewWhere("SELECT 1"), &res) 22 | if !has || res != 1 { 23 | c.JSON(503, gin.H{"error": "mysql do not respond"}) 24 | 25 | return 26 | } 27 | 28 | ormService.GetRedis().Set("ping", 1, helper.Minute*time.Second) 29 | 30 | _, has = ormService.GetRedis().Get("ping") 31 | if !has { 32 | c.JSON(503, gin.H{"error": "redis do not respond"}) 33 | 34 | return 35 | } 36 | 37 | c.JSON(200, "OK") 38 | } 39 | 40 | func (controller *ReadinessController) GetLivenessAction(c *gin.Context) { 41 | c.JSON(200, "OK") 42 | } 43 | -------------------------------------------------------------------------------- /pkg/dto/acl/permission.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | type PermissionResponseDTO struct { 4 | ID uint64 5 | Name string 6 | } 7 | -------------------------------------------------------------------------------- /pkg/dto/acl/resource.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | type ResourcesResponseDTO struct { 4 | Resources []*ResourceResponseDTO 5 | } 6 | 7 | type ResourceResponseDTO struct { 8 | ID uint64 9 | Name string 10 | Permissions []*PermissionResponseDTO 11 | } 12 | -------------------------------------------------------------------------------- /pkg/dto/acl/role.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | import "github.com/coretrix/hitrix/service/component/crud" 4 | 5 | type RolesResponseDTO struct { 6 | Rows []*RoleResponseDTO 7 | Total int 8 | Columns []*crud.Column 9 | PageContext *ResourcesResponseDTO 10 | } 11 | 12 | type RoleResponseDTO struct { 13 | ID uint64 14 | Name string 15 | Resources []*ResourceResponseDTO `json:",omitempty"` 16 | } 17 | 18 | type RoleRequestDTO struct { 19 | ID uint64 `binding:"required"` 20 | } 21 | 22 | type CreateOrUpdateRoleRequestDTO struct { 23 | Name string `binding:"required"` 24 | Resources []*RoleResourceRequestDTO `binding:"required,min=1,dive"` 25 | } 26 | 27 | type RoleResourceRequestDTO struct { 28 | ResourceID uint64 `binding:"required"` 29 | PermissionIDs []uint64 `binding:"required"` 30 | } 31 | 32 | type RolePermissionRequestDTO struct { 33 | PermissionID uint64 `binding:"required"` 34 | } 35 | 36 | type AssignRoleToUserRequestDTO struct { 37 | UserID uint64 `binding:"required"` 38 | RoleID uint64 `binding:"required"` 39 | } 40 | -------------------------------------------------------------------------------- /pkg/dto/file/image.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | type Image struct { 4 | ID uint64 `json:",omitempty"` 5 | URL string 6 | Namespace string 7 | Primary bool 8 | Hidden bool 9 | } 10 | -------------------------------------------------------------------------------- /pkg/dto/indexes/indexes.go: -------------------------------------------------------------------------------- 1 | package indexes 2 | 3 | type ResponseDTOList struct { 4 | Indexes []Index 5 | } 6 | 7 | type Index struct { 8 | Name string 9 | TotalDocs uint64 10 | TotalSize uint64 11 | } 12 | -------------------------------------------------------------------------------- /pkg/dto/list/list.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | type RequestDTOList struct { 4 | Page *int `binding:"required"` 5 | PageSize *int `binding:"required"` 6 | Search map[string]interface{} 7 | SearchOR map[string]interface{} 8 | Sort map[string]interface{} 9 | } 10 | -------------------------------------------------------------------------------- /pkg/dto/requestlogger/request_logger.go: -------------------------------------------------------------------------------- 1 | package requestlogger 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/coretrix/hitrix/service/component/crud" 7 | ) 8 | 9 | type ResponseDTORequestLoggerListDevPanel struct { 10 | Rows []*ResponseDTORequestLogger 11 | Total int 12 | Columns []*crud.Column 13 | } 14 | 15 | type ResponseDTORequestLogger struct { 16 | ID uint64 17 | UserID uint64 18 | URL string 19 | AppName string 20 | Request string 21 | Response string 22 | Log *string 23 | Status int 24 | RequestDuration int64 25 | CreatedAt time.Time 26 | } 27 | -------------------------------------------------------------------------------- /pkg/dto/translation/translation.go: -------------------------------------------------------------------------------- 1 | package translation 2 | 3 | import ( 4 | "github.com/coretrix/hitrix/pkg/entity" 5 | "github.com/coretrix/hitrix/service/component/crud" 6 | ) 7 | 8 | type RequestCreateTranslation struct { 9 | Key entity.TranslationTextKey `binding:"required"` 10 | Lang entity.TranslationTextLang `binding:"required"` 11 | Text string `binding:"required"` 12 | } 13 | 14 | type RequestUpdateTranslation struct { 15 | Key entity.TranslationTextKey `binding:"required"` 16 | Lang entity.TranslationTextLang `binding:"required"` 17 | Text string `binding:"required"` 18 | } 19 | 20 | type RequestDTOTranslationID struct { 21 | ID uint64 `uri:"ID" binding:"required" example:"1"` 22 | } 23 | 24 | type ResponseTranslation struct { 25 | ID uint64 26 | Lang string 27 | Key string 28 | Text string 29 | Variables string 30 | } 31 | 32 | type ResponseDTOList struct { 33 | Rows []*ListRow 34 | Total int 35 | Columns []*crud.Column 36 | } 37 | 38 | type ListRow struct { 39 | ID uint64 40 | Status string 41 | Lang string 42 | Key string 43 | Text string 44 | } 45 | -------------------------------------------------------------------------------- /pkg/entity/feature_flag_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | ) 8 | 9 | type FeatureFlagEntity struct { 10 | beeorm.ORM `orm:"table=feature_flags;redisCache;redisSearch=search_pool"` 11 | ID uint64 12 | Name string `orm:"length=100;required;unique=Name;searchable"` 13 | Registered bool `orm:"searchable"` 14 | Enabled bool `orm:"searchable"` 15 | UpdatedAt *time.Time `orm:"time=true"` 16 | CreatedAt time.Time `orm:"time=true"` 17 | } 18 | -------------------------------------------------------------------------------- /pkg/entity/file_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | ) 8 | 9 | type FileStatus string 10 | 11 | func (f FileStatus) String() string { 12 | return string(f) 13 | } 14 | 15 | const ( 16 | FileStatusNew FileStatus = "new" 17 | FileStatusProcessed FileStatus = "processed" 18 | ) 19 | 20 | type fileStatus struct { 21 | FileStatusNew string 22 | FileStatusProcessed string 23 | } 24 | 25 | var FileStatusAll = fileStatus{ 26 | FileStatusNew: FileStatusNew.String(), 27 | FileStatusProcessed: FileStatusProcessed.String(), 28 | } 29 | 30 | type FileObject struct { 31 | ID uint64 32 | StorageKey string 33 | Data interface{} 34 | } 35 | 36 | type FileEntity struct { 37 | beeorm.ORM `orm:"table=files;redisSearch=search_pool"` 38 | ID uint64 `orm:"searchable;sortable"` 39 | File *FileObject 40 | Status string `orm:"required;enum=entity.FileStatusAll"` 41 | Namespace string `orm:"required;searchable"` 42 | CreatedAt time.Time `orm:"time=true"` 43 | } 44 | -------------------------------------------------------------------------------- /pkg/entity/geocoding_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | ) 8 | 9 | type GeocodingCacheEntity struct { 10 | beeorm.ORM `orm:"table=geocoding_cache;redisCache"` 11 | ID uint64 12 | Lat float64 `orm:"decimal=8,5"` 13 | Lng float64 `orm:"decimal=8,5"` 14 | Address string `orm:"required"` 15 | AddressHash string `orm:"length=32;required;unique=AddressHash_Language:1"` 16 | Language string `orm:"required;enum=entity.LanguageValueAll;unique=AddressHash_Language:2"` 17 | Provider string 18 | RawResponse interface{} 19 | ExpiresAt time.Time `orm:"time=true;index=ExpiresAt"` 20 | CreatedAt time.Time `orm:"time=true"` 21 | 22 | CachedQueryAddressHashLanguage *beeorm.CachedQuery `queryOne:":AddressHash = ? AND :Language = ?"` 23 | } 24 | -------------------------------------------------------------------------------- /pkg/entity/geocoding_reverse_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | ) 8 | 9 | type GeocodingReverseCacheEntity struct { 10 | beeorm.ORM `orm:"table=geocoding_reverse_cache;redisCache"` 11 | ID uint64 12 | Lat float64 `orm:"decimal=8,5;required;unique=Lat_Lng_Language:1"` 13 | Lng float64 `orm:"decimal=8,5;required;unique=Lat_Lng_Language:2"` 14 | Address string 15 | Language string `orm:"required;enum=entity.LanguageValueAll;unique=Lat_Lng_Language:3"` 16 | Provider string 17 | RawResponse interface{} 18 | ExpiresAt time.Time `orm:"time=true;index=ExpiresAt"` 19 | CreatedAt time.Time `orm:"time=true"` 20 | 21 | CachedQueryLatLngLanguage *beeorm.CachedQuery `queryOne:":Lat = ? AND :Lng = ? AND :Language = ?"` 22 | } 23 | -------------------------------------------------------------------------------- /pkg/entity/language_enum.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | const ( 4 | LanguageEnglish = "en" 5 | LanguageBulgarian = "bg" 6 | ) 7 | 8 | type languageValue struct { 9 | LanguageEnglish string 10 | LanguageBulgarian string 11 | } 12 | 13 | var LanguageValueAll = languageValue{ 14 | LanguageEnglish: LanguageEnglish, 15 | LanguageBulgarian: LanguageBulgarian, 16 | } 17 | -------------------------------------------------------------------------------- /pkg/entity/mail_tracker_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | ) 8 | 9 | const ( 10 | MailTrackerStatusNew = "new" 11 | MailTrackerStatusQueued = "queued" 12 | MailTrackerStatusSuccess = "success" 13 | MailTrackerStatusError = "error" 14 | ) 15 | 16 | type mailTrackerStatus struct { 17 | MailTrackerStatusSuccess string 18 | MailTrackerStatusError string 19 | MailTrackerStatusQueued string 20 | } 21 | 22 | var MailTrackerStatusAll = mailTrackerStatus{ 23 | MailTrackerStatusSuccess: MailTrackerStatusSuccess, 24 | MailTrackerStatusError: MailTrackerStatusError, 25 | MailTrackerStatusQueued: MailTrackerStatusQueued, 26 | } 27 | 28 | type MailTrackerEntity struct { 29 | beeorm.ORM `orm:"table=email_tracker"` 30 | ID uint64 31 | Status string `orm:"enum=entity.MailTrackerStatusAll"` 32 | From string `orm:"varchar=255"` 33 | To string `orm:"varchar=255"` 34 | Subject string 35 | TemplateFile string 36 | TemplateData string `orm:"length=max"` 37 | SenderError string 38 | ReadAt *time.Time `orm:"time"` 39 | CreatedAt time.Time `orm:"time"` 40 | } 41 | -------------------------------------------------------------------------------- /pkg/entity/oss_bucket_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/latolukasz/beeorm/v2" 4 | 5 | type OSSBucketCounterEntity struct { 6 | beeorm.ORM `orm:"table=oss_buckets_counters"` 7 | ID uint64 8 | Counter uint64 `orm:"required"` 9 | } 10 | -------------------------------------------------------------------------------- /pkg/entity/permission_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | ) 8 | 9 | type PermissionEntity struct { 10 | beeorm.ORM `orm:"table=permissions;redisCache;redisSearch=search_pool"` 11 | ID uint64 `orm:"searchable;sortable"` 12 | ResourceID *ResourceEntity `orm:"required;searchable;unique=ResourceID_Name_FakeDelete:1"` 13 | Name string `orm:"required;searchable;unique=ResourceID_Name_FakeDelete:2"` 14 | CreatedAt time.Time `orm:"time=true"` 15 | FakeDelete bool `orm:"unique=ResourceID_Name_FakeDelete:3"` 16 | } 17 | -------------------------------------------------------------------------------- /pkg/entity/privilege_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | ) 8 | 9 | type PrivilegeEntity struct { 10 | beeorm.ORM `orm:"table=privileges;redisCache;redisSearch=search_pool"` 11 | ID uint64 12 | RoleID *RoleEntity `orm:"required;searchable;unique=RoleID_ResourceID_FakeDelete:1"` 13 | ResourceID *ResourceEntity `orm:"required;searchable;unique=RoleID_ResourceID_FakeDelete:2"` 14 | PermissionIDs []*PermissionEntity `orm:"required;searchable"` 15 | CreatedAt time.Time `orm:"time=true"` 16 | FakeDelete bool `orm:"unique=RoleID_ResourceID_FakeDelete:3"` 17 | } 18 | -------------------------------------------------------------------------------- /pkg/entity/request_logger_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | ) 8 | 9 | type RequestLoggerEntity struct { 10 | beeorm.ORM `orm:"table=request_logger"` 11 | ID uint64 12 | URL string `orm:"length=500;index=URL"` 13 | UserID uint64 `orm:"index=UserID"` 14 | AppName string `orm:"required;index=AppName"` 15 | Request []byte `orm:"mediumblob"` 16 | Response []byte `orm:"mediumblob"` 17 | Log []byte `orm:"mediumblob"` 18 | Status int 19 | RequestDuration int64 20 | CreatedAt time.Time `orm:"time=true;index=CreatedAt"` 21 | } 22 | -------------------------------------------------------------------------------- /pkg/entity/resource_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | ) 8 | 9 | type ResourceEntity struct { 10 | beeorm.ORM `orm:"table=resources;redisCache;redisSearch=search_pool"` 11 | ID uint64 `orm:"searchable"` 12 | Name string `orm:"required;searchable;unique=Name_FakeDelete:1"` 13 | CreatedAt time.Time `orm:"time=true"` 14 | FakeDelete bool `orm:"unique=Name_FakeDelete:2"` 15 | } 16 | -------------------------------------------------------------------------------- /pkg/entity/role_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | ) 8 | 9 | type RoleEntity struct { 10 | beeorm.ORM `orm:"table=roles;redisCache;redisSearch=search_pool"` 11 | ID uint64 `orm:"sortable"` 12 | Name string `orm:"required;searchable;unique=Name_FakeDelete:1"` 13 | IsPredefined bool `orm:"searchable"` 14 | CreatedAt time.Time `orm:"time=true"` 15 | FakeDelete bool `orm:"unique=Name_FakeDelete:2"` 16 | } 17 | -------------------------------------------------------------------------------- /pkg/entity/seeder_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | ) 8 | 9 | type SeederEntity struct { 10 | beeorm.ORM `orm:"table=seeder;redisCache;redisSearch=search_pool;"` 11 | ID uint64 12 | Name string `orm:"required;unique=Seeder_Name;searchable;"` 13 | CreatedAt time.Time `orm:"time=true"` 14 | } 15 | -------------------------------------------------------------------------------- /pkg/entity/translation_text_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/latolukasz/beeorm/v2" 4 | 5 | type TranslationTextLang string 6 | 7 | func (u TranslationTextLang) String() string { 8 | return string(u) 9 | } 10 | 11 | type TranslationTextKey string 12 | 13 | func (u TranslationTextKey) String() string { 14 | return string(u) 15 | } 16 | 17 | type TranslationStatus string 18 | 19 | func (u TranslationStatus) String() string { 20 | return string(u) 21 | } 22 | 23 | const ( 24 | TranslationStatusNew TranslationStatus = "new" 25 | TranslationStatusTranslated TranslationStatus = "translated" 26 | ) 27 | 28 | type translationStatus struct { 29 | New string 30 | Translated string 31 | } 32 | 33 | var TranslationStatusAll = translationStatus{ 34 | New: TranslationStatusNew.String(), 35 | Translated: TranslationStatusTranslated.String(), 36 | } 37 | 38 | type TranslationTextEntity struct { 39 | beeorm.ORM `orm:"table=translation_texts;log=log_db_pool;redisCache;localCache"` 40 | ID uint64 41 | Lang string `orm:"required;unique=Lang_Key:1"` 42 | Key string `orm:"required;unique=Lang_Key:2"` 43 | Status string `orm:"required;enum=entity.TranslationStatusAll"` 44 | Text string `orm:"length=max"` 45 | Vars []string 46 | 47 | CachedQueryLangKey *beeorm.CachedQuery `queryOne:":Lang = ? AND :Key = ?"` 48 | } 49 | -------------------------------------------------------------------------------- /pkg/error_handling/error_handling.go: -------------------------------------------------------------------------------- 1 | package errorhandling 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | "github.com/coretrix/hitrix/pkg/errors" 7 | "github.com/coretrix/hitrix/pkg/response" 8 | ) 9 | 10 | func HandleError(c *gin.Context, err error) bool { 11 | errType, ok := err.(errors.FieldErrors) 12 | if ok && errType != nil { 13 | response.ErrorResponseFields(c, errType, nil) 14 | 15 | return true 16 | } 17 | 18 | if err != nil { 19 | response.ErrorResponseGlobal(c, err, nil) 20 | 21 | return true 22 | } 23 | 24 | return false 25 | } 26 | -------------------------------------------------------------------------------- /pkg/graphql/depth/depth.go: -------------------------------------------------------------------------------- 1 | package depth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/99designs/gqlgen/graphql" 7 | "github.com/vektah/gqlparser/v2/ast" 8 | ) 9 | 10 | func GetOperationDepth(ctx context.Context) int { 11 | return findSelectionDepth(graphql.GetOperationContext(ctx).Operation.SelectionSet) 12 | } 13 | func findSelectionDepth(selections ast.SelectionSet) int { 14 | maxDepth := 0 15 | 16 | for _, selection := range selections { 17 | if field, isField := selection.(*ast.Field); isField && field != nil { 18 | if len(field.SelectionSet) > 0 { 19 | if depth := findSelectionDepth(field.SelectionSet); depth+1 > maxDepth { 20 | maxDepth = depth + 1 21 | } 22 | } 23 | } else if fragment, isFragmentSpread := selection.(*ast.FragmentSpread); isFragmentSpread && fragment != nil { 24 | if len(fragment.Definition.SelectionSet) > 0 { 25 | if depth := findSelectionDepth(fragment.Definition.SelectionSet); depth+1 > maxDepth { 26 | maxDepth = depth + 1 27 | } 28 | } 29 | } else if inlineFragment, isInlineFragment := selection.(*ast.InlineFragment); isInlineFragment && inlineFragment != nil { 30 | if len(inlineFragment.SelectionSet) > 0 { 31 | if depth := findSelectionDepth(inlineFragment.SelectionSet); depth+1 > maxDepth { 32 | maxDepth = depth + 1 33 | } 34 | } 35 | } 36 | } 37 | 38 | return maxDepth 39 | } 40 | -------------------------------------------------------------------------------- /pkg/graphql/scalars/json.go: -------------------------------------------------------------------------------- 1 | package scalars 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | 8 | "github.com/99designs/gqlgen/graphql" 9 | ) 10 | 11 | func MarshalJSON(json json.RawMessage) graphql.Marshaler { 12 | return graphql.WriterFunc(func(w io.Writer) { 13 | _, _ = io.WriteString(w, string(json)) 14 | }) 15 | } 16 | 17 | func UnmarshalJSON(v interface{}) (json.RawMessage, error) { 18 | switch v := v.(type) { 19 | case json.RawMessage: 20 | return v, nil 21 | case []byte: 22 | return v, nil 23 | default: 24 | return nil, fmt.Errorf("%T is not json", v) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pkg/graphql/scalars/mapUint64.go: -------------------------------------------------------------------------------- 1 | package scalars 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | 8 | "github.com/99designs/gqlgen/graphql" 9 | ) 10 | 11 | func MarshalMapUint64(m map[uint64]interface{}) graphql.Marshaler { 12 | return graphql.WriterFunc(func(w io.Writer) { 13 | marshaled, err := json.Marshal(m) 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | _, err = w.Write(marshaled) 19 | if err != nil { 20 | panic(err) 21 | } 22 | }) 23 | } 24 | 25 | func UnmarshalMapUint64(v interface{}) (map[uint64]interface{}, error) { 26 | switch v := v.(type) { 27 | case map[uint64]interface{}: 28 | return v, nil 29 | case json.RawMessage: 30 | m := map[uint64]interface{}{} 31 | if err := json.Unmarshal(v, &m); err != nil { 32 | return nil, err 33 | } 34 | 35 | return m, nil 36 | case string: 37 | m := map[uint64]interface{}{} 38 | if err := json.Unmarshal([]byte(v), &m); err != nil { 39 | return nil, err 40 | } 41 | 42 | return m, nil 43 | default: 44 | return nil, fmt.Errorf("%T is not a map", v) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /pkg/graphql/scalars/uint64.go: -------------------------------------------------------------------------------- 1 | package scalars 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "strconv" 8 | 9 | "github.com/99designs/gqlgen/graphql" 10 | ) 11 | 12 | func MarshalUint64(i uint64) graphql.Marshaler { 13 | return graphql.WriterFunc(func(w io.Writer) { 14 | _, _ = io.WriteString(w, fmt.Sprintf("%d", i)) 15 | }) 16 | } 17 | 18 | func UnmarshalUint64(v interface{}) (uint64, error) { 19 | switch v := v.(type) { 20 | case string: 21 | i, err := strconv.Atoi(v) 22 | if err != nil { 23 | return 0, fmt.Errorf("string failed to be parsed: %v", err) 24 | } 25 | 26 | return uint64(i), nil 27 | case int: 28 | return uint64(v), nil 29 | case int64: 30 | return uint64(v), nil 31 | case json.Number: 32 | i, err := strconv.Atoi(string(v)) 33 | if err != nil { 34 | return 0, fmt.Errorf("json.Number failed to be parsed: %v", err) 35 | } 36 | 37 | return uint64(i), nil 38 | default: 39 | return 0, fmt.Errorf("%T is not an int", v) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pkg/helper/array_test.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestSubtractUInt64Slice(t *testing.T) { 10 | a := []uint64{1, 2, 3} 11 | b := []uint64{1} 12 | res := SubtractUInt64Slice(a, b) 13 | assert.Equal(t, []uint64{2, 3}, res) 14 | } 15 | 16 | func TestSubtractInt64Slice(t *testing.T) { 17 | a := []int64{1, 2, 3} 18 | b := []int64{1} 19 | res := SubtractInt64Slice(a, b) 20 | assert.Equal(t, []int64{2, 3}, res) 21 | } 22 | 23 | func TestSubtractUInt32Slice(t *testing.T) { 24 | var a []uint32 25 | b := []uint32{1} 26 | res := SubtractUInt32Slice(a, b) 27 | assert.Equal(t, []uint32{}, res) 28 | } 29 | 30 | func TestSubtractInt32Slice(t *testing.T) { 31 | a := []int32{1, 2, 3} 32 | var b []int32 33 | res := SubtractInt32Slice(a, b) 34 | assert.Equal(t, a, res) 35 | } 36 | 37 | func TestSubtractUIntSlice(t *testing.T) { 38 | a := []uint{1, 2, 3} 39 | b := []uint{10, 20} 40 | res := SubtractUIntSlice(a, b) 41 | assert.Equal(t, a, res) 42 | } 43 | 44 | func TestSubtractIntSlice(t *testing.T) { 45 | a := []int{1, 2, 3} 46 | b := []int{10, 20} 47 | res := SubtractIntSlice(a, b) 48 | assert.Equal(t, a, res) 49 | } 50 | -------------------------------------------------------------------------------- /pkg/helper/auth.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import "encoding/base64" 4 | 5 | func BasicAuth(username, password string) string { 6 | return base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) 7 | } 8 | -------------------------------------------------------------------------------- /pkg/helper/cdn/cdn.go: -------------------------------------------------------------------------------- 1 | package cdn 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | const ( 10 | Options = "fit=${fit},format=${format},metadata=none,onerror=redirect,quality=${quality},width=${width},dpr=${dpr}/" 11 | ) 12 | 13 | func GetImageURLTemplate(image string) string { 14 | return service.DI().Config().MustString("oss.cdn_url") + Options + image 15 | } 16 | 17 | func GetImageURLTemplateFilled(image, fit, format, quality, width, dpr string) string { 18 | image = service.DI().Config().MustString("oss.cdn_url") + Options + image 19 | image = strings.Replace(image, "${fit}", fit, -1) 20 | image = strings.Replace(image, "${format}", format, -1) 21 | image = strings.Replace(image, "${quality}", quality, -1) 22 | image = strings.Replace(image, "${width}", width, -1) 23 | image = strings.Replace(image, "${dpr}", dpr, -1) 24 | 25 | return image 26 | } 27 | -------------------------------------------------------------------------------- /pkg/helper/float_precision.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import "math" 4 | 5 | func ToFixed(num float64, precision int) float64 { 6 | output := math.Pow(10, float64(precision)) 7 | 8 | return float64(round(num*output)) / output 9 | } 10 | 11 | func round(num float64) int { 12 | return int(num + math.Copysign(0.5, num)) 13 | } 14 | -------------------------------------------------------------------------------- /pkg/helper/geo.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | type Coordinates struct { 8 | Latitude float64 9 | Longitude float64 10 | } 11 | 12 | const radius = 6371 // Earth's mean radius in kilometers 13 | 14 | func degrees2radians(degrees float64) float64 { 15 | return degrees * math.Pi / 180 16 | } 17 | 18 | func (origin Coordinates) DistanceInKm(destination Coordinates) float64 { 19 | degreesLat := degrees2radians(destination.Latitude - origin.Latitude) 20 | degreesLong := degrees2radians(destination.Longitude - origin.Longitude) 21 | a := math.Sin(degreesLat/2)*math.Sin(degreesLat/2) + 22 | math.Cos(degrees2radians(origin.Latitude))* 23 | math.Cos(degrees2radians(destination.Latitude))*math.Sin(degreesLong/2)* 24 | math.Sin(degreesLong/2) 25 | c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) 26 | d := radius * c 27 | 28 | return d 29 | } 30 | 31 | func (origin Coordinates) DistanceInMeters(destination Coordinates) float64 { 32 | return origin.DistanceInKm(destination) * 1000 33 | } 34 | -------------------------------------------------------------------------------- /pkg/helper/mysql.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | 8 | "github.com/coretrix/hitrix/datalayer" 9 | ) 10 | 11 | type transaction func() error 12 | 13 | func DBTransaction(ormService *datalayer.ORM, callback transaction) error { 14 | dbService := ormService.GetMysql() 15 | 16 | dbService.Begin() 17 | 18 | err := callback() 19 | if err != nil { 20 | dbService.Rollback() 21 | 22 | return err 23 | } 24 | 25 | dbService.Commit() 26 | 27 | return nil 28 | } 29 | 30 | func Limit(pager *beeorm.Pager) string { 31 | return fmt.Sprintf("LIMIT %d,%d", (pager.CurrentPage-1)*pager.PageSize, pager.PageSize) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/helper/pagination.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | type URLQueryPager struct { 4 | // example = ?current_page=1&page_size=25 5 | CurrentPage int `binding:"min=1" form:"current_page"` 6 | PageSize int `binding:"min=1" form:"page_size"` 7 | } 8 | -------------------------------------------------------------------------------- /pkg/helper/phone.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | func GetPhoneWithPlus(mobile string) string { 4 | return "+" + mobile 5 | } 6 | -------------------------------------------------------------------------------- /pkg/helper/price_test.go: -------------------------------------------------------------------------------- 1 | package helper_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/coretrix/hitrix/pkg/helper" 9 | ) 10 | 11 | func TestNewPrice(t *testing.T) { 12 | price := helper.NewPrice(229.90) 13 | 14 | assert.Equal(t, 229.90, price.Float()) 15 | assert.Equal(t, int64(229900), price.Units()) 16 | assert.Equal(t, "229.90", price.String()) 17 | } 18 | 19 | func TestNewTotalPrice(t *testing.T) { 20 | price := helper.NewTotalPrice(10.29, 3) 21 | 22 | assert.Equal(t, 30.87, price.Float()) 23 | assert.Equal(t, int64(30870), price.Units()) 24 | assert.Equal(t, "30.87", price.String()) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/middleware/acl_router.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/latolukasz/beeorm/v2" 6 | 7 | "github.com/coretrix/hitrix/example/entity" 8 | "github.com/coretrix/hitrix/pkg/controller" 9 | ) 10 | 11 | func ACLRouter(ginEngine *gin.Engine) { 12 | v1Group := ginEngine.Group("/v1/") 13 | 14 | var aclController *controller.ACLController 15 | { 16 | aclGroup := v1Group.Group("/acl") 17 | 18 | aclGroup.GET("/resources/", aclController.ListResourcesAction) 19 | aclGroup.GET("/role/:ID/", aclController.GetRoleAction) 20 | aclGroup.POST("/roles/", aclController.ListRolesAction) 21 | aclGroup.POST("/role/", aclController.CreateRoleAction) 22 | aclGroup.PUT("/role/:ID/", aclController.UpdateRoleAction) 23 | aclGroup.DELETE("/role/:ID/", aclController.DeleteRoleAction) 24 | aclGroup.POST("/assign-role/", aclController.PostAssignRoleToUserAction(func() beeorm.Entity { 25 | return &entity.AdminUserEntity{} 26 | })) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pkg/middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/gin-contrib/cors" 7 | "github.com/gin-gonic/gin" 8 | 9 | "github.com/coretrix/hitrix/service" 10 | ) 11 | 12 | func Cors(ginEngine *gin.Engine) { 13 | origins, ok := service.DI().Config().Strings("cors") 14 | if !ok { 15 | panic("cors is missing") 16 | } 17 | 18 | corsConfig := cors.Config{ 19 | AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"}, 20 | AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization", "X-Requested-With", "User-Agent", "X-Lang"}, 21 | AllowOrigins: origins, 22 | AllowCredentials: true, 23 | MaxAge: 12 * time.Hour, 24 | ExposeHeaders: []string{"X-Invalid-Authorization", "X-Clockwork-Id", "X-Clockwork-Version"}, 25 | } 26 | 27 | ginEngine.Use(cors.New(corsConfig)) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/middleware/dev_auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | 8 | "github.com/coretrix/hitrix/pkg/view/account" 9 | ) 10 | 11 | func AuthorizeDevUser() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | err := account.IsValidDevToken(c, c.Request.Header.Get("Authorization")) 14 | if err != nil { 15 | c.AbortWithStatus(http.StatusUnauthorized) 16 | 17 | return 18 | } 19 | } 20 | } 21 | 22 | func AuthorizeWithDevRefreshToken() gin.HandlerFunc { 23 | return func(c *gin.Context) { 24 | err := account.IsValidDevRefreshToken(c, c.Request.Header.Get("Authorization")) 25 | if err != nil { 26 | c.AbortWithStatus(http.StatusUnauthorized) 27 | 28 | return 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pkg/middleware/file_router.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | "github.com/coretrix/hitrix/pkg/controller" 7 | ) 8 | 9 | func FileRouter(ginEngine *gin.Engine) { 10 | v1Group := ginEngine.Group("/v1/") 11 | 12 | var fileController *controller.FileController 13 | fileGroup := v1Group.Group("file/") 14 | { 15 | fileGroup.POST("upload/", fileController.PostUploadImageAction) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /pkg/middleware/url_auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | 8 | "github.com/coretrix/hitrix/service" 9 | ) 10 | 11 | func AuthorizeWithQueryParam() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | secretFromQueryString := c.Query("Secret") 14 | 15 | secretFromConfig, ok := service.DI().Config().String("query_param.secret") 16 | if !ok { 17 | c.AbortWithStatus(http.StatusUnauthorized) 18 | 19 | return 20 | } 21 | 22 | if secretFromConfig != secretFromQueryString { 23 | c.AbortWithStatus(http.StatusUnauthorized) 24 | 25 | return 26 | } 27 | 28 | c.Next() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pkg/model/account/login_dev.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/gin-gonic/gin" 7 | 8 | "github.com/coretrix/hitrix/pkg/binding" 9 | "github.com/coretrix/hitrix/pkg/view/account" 10 | "github.com/coretrix/hitrix/service" 11 | ) 12 | 13 | type LoginDevForm struct { 14 | Username string `binding:"required,min=6,max=60" json:"Username" form:"username"` 15 | Password string `binding:"required,min=8,max=60" json:"Password" form:"password"` 16 | } 17 | 18 | func (l *LoginDevForm) Login(c *gin.Context) (string, string, error) { 19 | err := binding.ShouldBindJSON(c, l) 20 | if err != nil { 21 | return "", "", err 22 | } 23 | 24 | ormService := service.DI().OrmEngineForContext(c.Request.Context()) 25 | 26 | passwordService := service.DI().Password() 27 | 28 | devPanelUserEntity := service.DI().App().DevPanel.UserEntity 29 | ok := ormService.CachedSearchOne(devPanelUserEntity, "UserEmailIndex", l.Username) 30 | 31 | if !ok { 32 | return "", "", errors.New("invalid username or password") 33 | } 34 | 35 | if !passwordService.VerifyPassword(l.Password, devPanelUserEntity.GetPassword()) { 36 | return "", "", errors.New("invalid username or password") 37 | } 38 | 39 | token, refreshToken, err := account.GenerateDevTokenAndRefreshToken(ormService, devPanelUserEntity.GetID()) 40 | if err != nil { 41 | return "", "", err 42 | } 43 | 44 | return token, refreshToken, err 45 | } 46 | -------------------------------------------------------------------------------- /pkg/model/translation/create.go: -------------------------------------------------------------------------------- 1 | package translation 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/coretrix/hitrix/pkg/dto/translation" 7 | "github.com/coretrix/hitrix/pkg/entity" 8 | "github.com/coretrix/hitrix/pkg/errors" 9 | "github.com/coretrix/hitrix/service" 10 | ) 11 | 12 | func Create(ctx context.Context, request *translation.RequestCreateTranslation) (*translation.ResponseTranslation, error) { 13 | ormService := service.DI().OrmEngineForContext(ctx) 14 | 15 | newTranslationEntity := &entity.TranslationTextEntity{ 16 | Lang: request.Lang.String(), 17 | Key: request.Key.String(), 18 | Status: entity.TranslationStatusTranslated.String(), 19 | Text: request.Text, 20 | } 21 | 22 | err := ormService.FlushWithCheck(newTranslationEntity) 23 | if err != nil { 24 | return nil, errors.HandleFlushWithCheckError( 25 | err, 26 | errors.HandleCustomErrors(map[string]string{"Lang": "text with this lang and key already exists"}), 27 | ) 28 | } 29 | 30 | return &translation.ResponseTranslation{ 31 | ID: newTranslationEntity.ID, 32 | Lang: newTranslationEntity.Lang, 33 | Key: newTranslationEntity.Key, 34 | Text: newTranslationEntity.Text, 35 | }, nil 36 | } 37 | -------------------------------------------------------------------------------- /pkg/model/translation/delete.go: -------------------------------------------------------------------------------- 1 | package translation 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/coretrix/hitrix/pkg/entity" 8 | "github.com/coretrix/hitrix/service" 9 | ) 10 | 11 | func Delete(ctx context.Context, id uint64) error { 12 | ormService := service.DI().OrmEngineForContext(ctx) 13 | 14 | translationTextEntity := &entity.TranslationTextEntity{} 15 | found := ormService.LoadByID(id, translationTextEntity) 16 | 17 | if !found { 18 | return fmt.Errorf("translation text with ID %v not found", id) 19 | } 20 | 21 | ormService.Delete(translationTextEntity) 22 | 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /pkg/model/translation/update.go: -------------------------------------------------------------------------------- 1 | package translation 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/coretrix/hitrix/pkg/dto/translation" 8 | "github.com/coretrix/hitrix/pkg/entity" 9 | "github.com/coretrix/hitrix/pkg/errors" 10 | "github.com/coretrix/hitrix/service" 11 | ) 12 | 13 | func Update(ctx context.Context, request *translation.RequestUpdateTranslation, id uint64) (*translation.ResponseTranslation, error) { 14 | ormService := service.DI().OrmEngineForContext(ctx) 15 | 16 | translationTextEntity := &entity.TranslationTextEntity{} 17 | found := ormService.LoadByID(id, translationTextEntity) 18 | 19 | if !found { 20 | return nil, 21 | errors.HandleCustomErrors(map[string]string{"ID": fmt.Sprintf("City with id %v does not exists", id)}) 22 | } 23 | 24 | translationTextEntity.Lang = request.Lang.String() 25 | translationTextEntity.Key = request.Key.String() 26 | translationTextEntity.Text = request.Text 27 | translationTextEntity.Status = entity.TranslationStatusTranslated.String() 28 | 29 | err := ormService.FlushWithCheck(translationTextEntity) 30 | if err != nil { 31 | return nil, errors.HandleFlushWithCheckError( 32 | err, 33 | errors.HandleCustomErrors(map[string]string{"Lang": "text with this lang and key already exists"}), 34 | ) 35 | } 36 | 37 | return &translation.ResponseTranslation{ 38 | ID: translationTextEntity.ID, 39 | Lang: translationTextEntity.Lang, 40 | Key: translationTextEntity.Key, 41 | Text: translationTextEntity.Text, 42 | }, nil 43 | } 44 | -------------------------------------------------------------------------------- /pkg/queue/consumers/redisearch_reindex.go: -------------------------------------------------------------------------------- 1 | package consumers 2 | 3 | import ( 4 | redisearch "github.com/coretrix/beeorm-redisearch-plugin" 5 | "github.com/latolukasz/beeorm/v2" 6 | 7 | "github.com/coretrix/hitrix/datalayer" 8 | "github.com/coretrix/hitrix/pkg/queue/streams" 9 | ) 10 | 11 | type ReindexConsumer struct { 12 | redisearch *redisearch.RedisSearchEngine 13 | } 14 | 15 | func NewReindexConsumer(redisearch *redisearch.RedisSearchEngine) *ReindexConsumer { 16 | return &ReindexConsumer{redisearch: redisearch} 17 | } 18 | 19 | func (c *ReindexConsumer) GetQueueName() string { 20 | return redisearch.RedisSearchIndexerChannel 21 | } 22 | 23 | func (c *ReindexConsumer) GetGroupName(suffix *string) string { 24 | return streams.GetGroupName(c.GetQueueName(), suffix) 25 | } 26 | 27 | func (c *ReindexConsumer) Consume(_ *datalayer.ORM, event beeorm.Event) error { 28 | indexerEvent := &redisearch.IndexerEventRedisearch{} 29 | 30 | event.Unserialize(indexerEvent) 31 | 32 | c.redisearch.HandleRedisIndexerEvent(indexerEvent.Index) 33 | 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /pkg/queue/streams/streams.go: -------------------------------------------------------------------------------- 1 | package streams 2 | 3 | const StreamMsgRetryOTP = "msg.retry-otp" 4 | 5 | func GetGroupName(queueName string, suffix *string) string { 6 | if suffix == nil { 7 | return queueName + "_group" 8 | } 9 | 10 | return queueName + "_group_" + *suffix 11 | } 12 | -------------------------------------------------------------------------------- /pkg/queue/streams/streams_test.go: -------------------------------------------------------------------------------- 1 | package streams 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/xorcare/pointer" 8 | ) 9 | 10 | func TestGetGroupName(t *testing.T) { 11 | type testCase struct { 12 | streamName string 13 | suffix *string 14 | expectedGroupName string 15 | } 16 | 17 | testCases := []testCase{ 18 | { 19 | streamName: "first", 20 | suffix: nil, 21 | expectedGroupName: "first_group", 22 | }, { 23 | streamName: "first", 24 | suffix: pointer.String("name"), 25 | expectedGroupName: "first_group_name", 26 | }, 27 | } 28 | 29 | for _, oneTestCase := range testCases { 30 | have := GetGroupName(oneTestCase.streamName, oneTestCase.suffix) 31 | assert.Equal(t, oneTestCase.expectedGroupName, have) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pkg/test/graphql-parser/error.go: -------------------------------------------------------------------------------- 1 | package graphqlparser 2 | 3 | type Errors []struct { 4 | Message string 5 | Path []string 6 | Locations []struct { 7 | Line int 8 | Column int 9 | } 10 | } 11 | 12 | func (e Errors) Error() string { 13 | return e[0].Message 14 | } 15 | -------------------------------------------------------------------------------- /pkg/test/graphql-parser/graphql.go: -------------------------------------------------------------------------------- 1 | package graphqlparser 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | ) 7 | 8 | type operationType uint8 9 | 10 | const ( 11 | queryOperation operationType = iota 12 | mutationOperation 13 | ) 14 | 15 | type QueryParser struct{} 16 | 17 | func NewQueryParser() *QueryParser { 18 | return &QueryParser{} 19 | } 20 | 21 | func (c *QueryParser) ParseQuery(q interface{}, variables map[string]interface{}) (bytes.Buffer, error) { 22 | return c.parse(queryOperation, q, variables) 23 | } 24 | 25 | func (c *QueryParser) ParseMutation(m interface{}, variables map[string]interface{}) (bytes.Buffer, error) { 26 | return c.parse(mutationOperation, m, variables) 27 | } 28 | 29 | func (c *QueryParser) parse(op operationType, v interface{}, variables map[string]interface{}) (bytes.Buffer, error) { 30 | var query string 31 | 32 | switch op { 33 | case queryOperation: 34 | query = constructQuery(v, variables) 35 | case mutationOperation: 36 | query = constructMutation(v, variables) 37 | } 38 | 39 | in := struct { 40 | Query string `json:"query"` 41 | Variables map[string]interface{} `json:"variables,omitempty"` 42 | }{ 43 | Query: query, 44 | Variables: variables, 45 | } 46 | 47 | var buff bytes.Buffer 48 | if err := json.NewEncoder(&buff).Encode(in); err != nil { 49 | return buff, err 50 | } 51 | 52 | return buff, nil 53 | } 54 | -------------------------------------------------------------------------------- /pkg/view/acl/acl.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | import ( 4 | redisearch "github.com/coretrix/beeorm-redisearch-plugin" 5 | "github.com/latolukasz/beeorm/v2" 6 | 7 | "github.com/coretrix/hitrix/datalayer" 8 | "github.com/coretrix/hitrix/pkg/entity" 9 | ) 10 | 11 | func ACL(ormService *datalayer.ORM, roleEntity *entity.RoleEntity, resource string, permissions ...string) bool { 12 | resourceQuery := redisearch.NewRedisSearchQuery() 13 | resourceQuery.FilterString("Name", resource) 14 | 15 | resourceEntity := &entity.ResourceEntity{} 16 | if !ormService.RedisSearchOne(resourceEntity, resourceQuery) { 17 | return false 18 | } 19 | 20 | permissionQuery := redisearch.NewRedisSearchQuery() 21 | permissionQuery.FilterUint("ResourceID", resourceEntity.ID) 22 | permissionQuery.FilterString("Name", permissions...) 23 | 24 | permissionEntities := make([]*entity.PermissionEntity, 0) 25 | ormService.RedisSearch(permissionQuery, beeorm.NewPager(1, 1000), &permissionEntities) 26 | 27 | if len(permissions) != len(permissionEntities) { 28 | return false 29 | } 30 | 31 | permissionIDs := make([]uint64, len(permissionEntities)) 32 | 33 | for i, permissionEntity := range permissionEntities { 34 | permissionIDs[i] = permissionEntity.ID 35 | } 36 | 37 | privilegeQuery := redisearch.NewRedisSearchQuery() 38 | privilegeQuery.FilterUint("RoleID", roleEntity.ID) 39 | privilegeQuery.FilterUint("ResourceID", resourceEntity.ID) 40 | privilegeQuery.FilterManyReferenceIn("PermissionIDs", permissionIDs...) 41 | 42 | return ormService.RedisSearchOne(&entity.PrivilegeEntity{}, privilegeQuery) 43 | } 44 | -------------------------------------------------------------------------------- /pkg/view/crud/validate.go: -------------------------------------------------------------------------------- 1 | package crud 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/coretrix/hitrix/pkg/dto/list" 8 | "github.com/coretrix/hitrix/service/component/crud" 9 | ) 10 | 11 | func ValidateListRequest(request list.RequestDTOList, pageSizeMin, pageSizeMax int) (*crud.ListRequest, error) { 12 | if request.Page == nil { 13 | return nil, errors.New("page is empty") 14 | } 15 | 16 | if request.PageSize == nil { 17 | return nil, errors.New("page size is empty") 18 | } 19 | 20 | if *request.Page < 1 { 21 | return nil, errors.New("page must be greater than or equal to 1") 22 | } 23 | 24 | if *request.PageSize < pageSizeMin { 25 | return nil, fmt.Errorf("page size must be greater than or equal to %v", pageSizeMin) 26 | } 27 | 28 | if *request.PageSize > pageSizeMax { 29 | return nil, fmt.Errorf("page size must be greater than or equal to %v", pageSizeMax) 30 | } 31 | 32 | return &crud.ListRequest{ 33 | Page: request.Page, 34 | PageSize: request.PageSize, 35 | Search: request.Search, 36 | SearchOR: request.SearchOR, 37 | Sort: request.Sort, 38 | }, nil 39 | } 40 | -------------------------------------------------------------------------------- /pkg/view/file/file.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "github.com/coretrix/hitrix/pkg/dto/file" 5 | "github.com/coretrix/hitrix/pkg/entity" 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/oss" 8 | ) 9 | 10 | type CDNInterface interface { 11 | GetImageURLWithTemplate(image string) string 12 | } 13 | 14 | func GetFileTypeCounter(fileObject *entity.FileObject, namespace oss.Namespace) *file.File { 15 | objectURL, err := service.DI().OSService().GetObjectURL(namespace, fileObject) 16 | if err != nil { 17 | panic(err) 18 | } 19 | 20 | return &file.File{ 21 | ID: fileObject.ID, 22 | Filename: fileObject.StorageKey, 23 | URL: objectURL, 24 | IDType: file.IDTypeOSSCounterID, 25 | } 26 | } 27 | 28 | func GetFileTypeCounterWithCDN(fileObject *entity.FileObject, namespace oss.Namespace, cdn CDNInterface) *file.File { 29 | objectURL, err := service.DI().OSService().GetObjectURL(namespace, fileObject) 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | return &file.File{ 35 | ID: fileObject.ID, 36 | Filename: fileObject.StorageKey, 37 | URL: cdn.GetImageURLWithTemplate(objectURL), 38 | IDType: file.IDTypeOSSCounterID, 39 | } 40 | } 41 | 42 | func GetFileTypeID(fileEntity *entity.FileEntity, namespace oss.Namespace) *file.File { 43 | objectURL, err := service.DI().OSService().GetObjectURL(namespace, fileEntity.File) 44 | if err != nil { 45 | panic(err) 46 | } 47 | 48 | return &file.File{ 49 | ID: fileEntity.ID, 50 | URL: objectURL, 51 | Namespace: oss.Namespace(fileEntity.Namespace), 52 | IDType: file.IDTypeFileID, 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /pkg/view/translation/get.go: -------------------------------------------------------------------------------- 1 | package translation 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/coretrix/hitrix/pkg/dto/translation" 9 | "github.com/coretrix/hitrix/pkg/entity" 10 | "github.com/coretrix/hitrix/pkg/errors" 11 | "github.com/coretrix/hitrix/service" 12 | ) 13 | 14 | func Get(ctx context.Context, id uint64) (*translation.ResponseTranslation, error) { 15 | ormService := service.DI().OrmEngineForContext(ctx) 16 | 17 | translationEntity := &entity.TranslationTextEntity{} 18 | 19 | found := ormService.LoadByID(id, translationEntity) 20 | 21 | if !found { 22 | return nil, errors.HandleCustomErrors(map[string]string{ 23 | "ID": fmt.Sprintf("%d does not exists", id), 24 | }) 25 | } 26 | 27 | return &translation.ResponseTranslation{ 28 | ID: translationEntity.ID, 29 | Lang: translationEntity.Lang, 30 | Key: translationEntity.Key, 31 | Text: translationEntity.Text, 32 | Variables: strings.Join(translationEntity.Vars, " "), 33 | }, nil 34 | } 35 | -------------------------------------------------------------------------------- /recovery.go: -------------------------------------------------------------------------------- 1 | package hitrix 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "net/http" 7 | 8 | "github.com/gin-gonic/gin" 9 | 10 | "github.com/coretrix/hitrix/service" 11 | ) 12 | 13 | func recovery() gin.HandlerFunc { 14 | return func(c *gin.Context) { 15 | defer func() { 16 | if r := recover(); r != nil { 17 | c.Status(http.StatusInternalServerError) 18 | c.Request.Body = io.NopCloser( 19 | bytes.NewReader(c.Request.Context().Value(service.RequestBodyKey).([]byte))) 20 | 21 | service.DI().ErrorLogger().LogErrorWithRequest(c, r) 22 | } 23 | }() 24 | 25 | c.Next() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /scripts/orm_alters.go: -------------------------------------------------------------------------------- 1 | package scripts 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/fatih/color" 7 | "github.com/sarulabs/di" 8 | 9 | "github.com/coretrix/hitrix/service" 10 | "github.com/coretrix/hitrix/service/component/app" 11 | ) 12 | 13 | func ORMAlters() *service.DefinitionGlobal { 14 | return &service.DefinitionGlobal{ 15 | Name: "orm-alters", 16 | 17 | Script: true, 18 | Build: func(ctn di.Container) (interface{}, error) { 19 | return &ORMAltersScript{}, nil 20 | }, 21 | } 22 | } 23 | 24 | type ORMAltersScript struct { 25 | } 26 | 27 | func (script *ORMAltersScript) Active() bool { 28 | _ = service.DI().OrmConfig() 29 | 30 | return true 31 | } 32 | 33 | func (script *ORMAltersScript) Unique() bool { 34 | return true 35 | } 36 | 37 | func (script *ORMAltersScript) Description() string { 38 | return "show all MySQL schema changes" 39 | } 40 | 41 | func (script *ORMAltersScript) Run(_ context.Context, exit app.IExit) { 42 | ormEngine := service.DI().OrmEngine() 43 | alters := ormEngine.GetAlters() 44 | 45 | for _, alter := range alters { 46 | if alter.Safe { 47 | color.Green("%s\n\n", alter.SQL) 48 | } else { 49 | color.Red("%s\n\n", alter.SQL) 50 | } 51 | } 52 | 53 | if len(alters) > 0 { 54 | exit.Error() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /scripts/redisearch_reindex_consumer.go: -------------------------------------------------------------------------------- 1 | package scripts 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/coretrix/hitrix/pkg/queue" 7 | "github.com/coretrix/hitrix/pkg/queue/consumers" 8 | "github.com/coretrix/hitrix/service" 9 | "github.com/coretrix/hitrix/service/component/app" 10 | ) 11 | 12 | type ReindexConsumerScript struct { 13 | } 14 | 15 | func (script *ReindexConsumerScript) Run(ctx context.Context, _ app.IExit) { 16 | queue.NewConsumerRunner(ctx).RunConsumerOne(consumers.NewReindexConsumer(service.DI().OrmEngine().RedisSearchEngine), nil, 1) 17 | } 18 | 19 | func (script *ReindexConsumerScript) Infinity() bool { 20 | return true 21 | } 22 | 23 | func (script *ReindexConsumerScript) Unique() bool { 24 | return true 25 | } 26 | 27 | func (script *ReindexConsumerScript) Description() string { 28 | return "redisearch reindex consumer" 29 | } 30 | -------------------------------------------------------------------------------- /scripts/retry_otp_consumer.go: -------------------------------------------------------------------------------- 1 | package scripts 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/coretrix/hitrix/pkg/queue" 7 | "github.com/coretrix/hitrix/pkg/queue/consumers" 8 | "github.com/coretrix/hitrix/service" 9 | "github.com/coretrix/hitrix/service/component/app" 10 | ) 11 | 12 | type RetryOTPConsumer struct { 13 | } 14 | 15 | func (script *RetryOTPConsumer) Run(ctx context.Context, _ app.IExit) { 16 | ormService := service.DI().OrmEngine() 17 | configService := service.DI().Config() 18 | otpService := service.DI().OTP() 19 | 20 | maxRetries, ok := configService.Int("sms.max_retries") 21 | if !ok { 22 | panic("missing sms.max_retries") 23 | } 24 | 25 | queue.NewConsumerRunner(ctx).RunConsumerOne(consumers.NewOTPRetryConsumer(ormService, maxRetries, otpService.GetGatewayRegistry()), nil, 1) 26 | } 27 | 28 | func (script *RetryOTPConsumer) Infinity() bool { 29 | return true 30 | } 31 | 32 | func (script *RetryOTPConsumer) Unique() bool { 33 | return true 34 | } 35 | 36 | func (script *RetryOTPConsumer) Description() string { 37 | return "retry otp consumer" 38 | } 39 | -------------------------------------------------------------------------------- /service/component/api_logger/api_logger.go: -------------------------------------------------------------------------------- 1 | package apilogger 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | 8 | "github.com/coretrix/hitrix/datalayer" 9 | ) 10 | 11 | type IAPILogger interface { 12 | LogStart(ormService *datalayer.ORM, logType string, request interface{}) 13 | LogError(ormService *datalayer.ORM, message string, response interface{}) 14 | LogSuccess(ormService *datalayer.ORM, response interface{}) 15 | } 16 | 17 | type ILogEntity interface { 18 | beeorm.Entity 19 | SetID(value uint64) 20 | SetType(value string) 21 | SetStatus(value string) 22 | SetRequest(value interface{}) 23 | SetResponse(value interface{}) 24 | SetMessage(value string) 25 | SetCreatedAt(value time.Time) 26 | } 27 | -------------------------------------------------------------------------------- /service/component/api_logger/mysql.go: -------------------------------------------------------------------------------- 1 | package apilogger 2 | 3 | import ( 4 | "reflect" 5 | "time" 6 | 7 | "github.com/coretrix/hitrix/datalayer" 8 | ) 9 | 10 | type mysqlDBLog struct { 11 | logEntity ILogEntity 12 | currentLog ILogEntity 13 | } 14 | 15 | func NewMysqlAPILogger(entity ILogEntity) IAPILogger { 16 | return &mysqlDBLog{logEntity: entity} 17 | } 18 | 19 | func (l *mysqlDBLog) LogStart(ormService *datalayer.ORM, logType string, request interface{}) { 20 | var logEntity ILogEntity 21 | 22 | if l.logEntity.GetID() == 0 { 23 | logEntity = l.logEntity 24 | } else { 25 | logEntity = reflect.New(reflect.ValueOf(l.logEntity).Elem().Type()).Interface().(ILogEntity) 26 | } 27 | 28 | logEntity.SetType(logType) 29 | logEntity.SetRequest(request) 30 | logEntity.SetStatus("new") 31 | logEntity.SetCreatedAt(time.Now()) 32 | 33 | ormService.Flush(logEntity) 34 | 35 | l.currentLog = logEntity 36 | } 37 | 38 | func (l *mysqlDBLog) LogError(ormService *datalayer.ORM, message string, response interface{}) { 39 | if l.currentLog == nil { 40 | panic("log is not created") 41 | } 42 | 43 | currentLog := l.currentLog 44 | currentLog.SetMessage(message) 45 | currentLog.SetResponse(response) 46 | currentLog.SetStatus("failed") 47 | 48 | ormService.Flush(currentLog) 49 | } 50 | 51 | func (l *mysqlDBLog) LogSuccess(ormService *datalayer.ORM, response interface{}) { 52 | if l.currentLog == nil { 53 | panic("log is not created") 54 | } 55 | 56 | currentLog := l.currentLog 57 | 58 | currentLog.SetStatus("completed") 59 | currentLog.SetResponse(response) 60 | 61 | ormService.Flush(currentLog) 62 | } 63 | -------------------------------------------------------------------------------- /service/component/app/flags.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "flag" 5 | ) 6 | 7 | type FlagsRegistry struct { 8 | Flags map[string]interface{} 9 | } 10 | 11 | type Flags struct { 12 | Registry *FlagsRegistry 13 | } 14 | 15 | func (r *FlagsRegistry) Bool(name string, value bool, usage string) { 16 | r.Flags[name] = flag.Bool(name, value, usage) 17 | } 18 | 19 | func (r *FlagsRegistry) String(name string, value string, usage string) { 20 | r.Flags[name] = flag.String(name, value, usage) 21 | } 22 | 23 | func (f *Flags) Bool(name string) bool { 24 | v, has := f.Registry.Flags[name] 25 | if !has { 26 | return false 27 | } 28 | 29 | return *v.(*bool) 30 | } 31 | 32 | func (f *Flags) String(name string) string { 33 | v, has := f.Registry.Flags[name] 34 | if !has { 35 | return "" 36 | } 37 | 38 | return *v.(*string) 39 | } 40 | -------------------------------------------------------------------------------- /service/component/app/script.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | type IScript interface { 9 | Description() string 10 | Run(ctx context.Context, exit IExit) 11 | Unique() bool 12 | } 13 | 14 | type IExit interface { 15 | Valid() 16 | Error() 17 | Custom(exitCode int) 18 | } 19 | 20 | type Infinity interface { 21 | Infinity() bool 22 | } 23 | 24 | type Interval interface { 25 | Interval() time.Duration 26 | } 27 | 28 | type IntervalOptional interface { 29 | IntervalActive() bool 30 | } 31 | 32 | type Intermediate interface { 33 | IsIntermediate() bool 34 | } 35 | 36 | type Optional interface { 37 | Active() bool 38 | } 39 | -------------------------------------------------------------------------------- /service/component/calendar/calendar.go: -------------------------------------------------------------------------------- 1 | package calendar 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "golang.org/x/oauth2" 6 | "google.golang.org/api/calendar/v3" 7 | 8 | "github.com/coretrix/hitrix/service/component/config" 9 | ) 10 | 11 | type NewCalendarFunc func(configService config.IConfig) (ICalendar, error) 12 | 13 | type ICalendar interface { 14 | GetAuthLink(state string) string 15 | GetStateCodeFromGin(c *gin.Context) (string, string, error) 16 | GetTokenFromCode(code string) (*oauth2.Token, error) 17 | RefreshToken(token *oauth2.Token) (bool, *oauth2.Token, error) 18 | GetCalendars(token *oauth2.Token) ([]*calendar.CalendarListEntry, error) 19 | GetCalendarEvents(token *oauth2.Token, calendarID string, args *GetCalendarEventsArgs) ([]*calendar.Event, error) 20 | UpsertEvent(token *oauth2.Token, calendarID string, event *calendar.Event) (*calendar.Event, error) 21 | } 22 | -------------------------------------------------------------------------------- /service/component/checkout/checkout.go: -------------------------------------------------------------------------------- 1 | package checkout 2 | 3 | import ( 4 | "github.com/checkout/checkout-sdk-go/instruments" 5 | "github.com/checkout/checkout-sdk-go/payments" 6 | "github.com/checkout/checkout-sdk-go/tokens" 7 | ) 8 | 9 | type ICheckout interface { 10 | CheckWebhookKey(keyCode, key string) bool 11 | RequestPayment(request *payments.Request) *payments.Response 12 | RequestRefunds(amount uint64, paymentID, reference string, metadata map[string]string) *payments.RefundsResponse 13 | DeleteCustomerInstrument(instrumentID string) bool 14 | GetCustomer(idOrEmail string) (bool, *CustomerResponse) 15 | SaveGetClient(customerData *SaveCustomerRequest) (created bool, customer *CustomerResponse) 16 | CreateToken(request *tokens.Request) (string, error) 17 | CreateInstrument(request *instruments.Request) (*instruments.Response, error) 18 | GetPaymentDetail(paymentID string) (*payments.PaymentResponse, error) 19 | GetInstrument(sourceID string) (*instruments.Response, error) 20 | } 21 | -------------------------------------------------------------------------------- /service/component/clock/clock.go: -------------------------------------------------------------------------------- 1 | package clock 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type IClock interface { 8 | Now() time.Time 9 | NowPointer() *time.Time 10 | } 11 | -------------------------------------------------------------------------------- /service/component/clock/mocks/clock.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/stretchr/testify/mock" 7 | ) 8 | 9 | type FakeSysClock struct { 10 | mock.Mock 11 | } 12 | 13 | func (c *FakeSysClock) Now() time.Time { 14 | return c.Called().Get(0).(time.Time) 15 | } 16 | 17 | func (c *FakeSysClock) NowPointer() *time.Time { 18 | return c.Called().Get(0).(*time.Time) 19 | } 20 | -------------------------------------------------------------------------------- /service/component/clock/sysclock.go: -------------------------------------------------------------------------------- 1 | package clock 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/xorcare/pointer" 7 | ) 8 | 9 | type SysClock struct{} 10 | 11 | func (c *SysClock) Now() time.Time { 12 | return time.Now().UTC().Truncate(time.Second) 13 | } 14 | 15 | func (c *SysClock) NowPointer() *time.Time { 16 | return pointer.Time(time.Now().UTC().Truncate(time.Second)) 17 | } 18 | 19 | func (c *SysClock) NowInLocation(loc *time.Location) time.Time { 20 | return c.Now().In(loc) 21 | } 22 | 23 | func (c *SysClock) NowInTimeZone() time.Time { 24 | return time.Now().UTC().Truncate(time.Second) 25 | } 26 | -------------------------------------------------------------------------------- /service/component/crud/const.go: -------------------------------------------------------------------------------- 1 | package crud 2 | 3 | const ( 4 | InputTypeNumber = "InputTypeNumber" 5 | MultiSelectTypeArrayNumber = "MultiSelectTypeArrayNumber" 6 | RangeSliderTypeArrayNumber = "RangeSliderTypeArrayNumber" 7 | InputTypeString = "InputTypeString" 8 | ArrayStringType = "ArrayStringType" //not sure what element should be, we can rename it whenever we need it 9 | SelectTypeStringString = "SelectTypeStringString" 10 | SelectTypeIntString = "SelectTypeIntString" 11 | DateTimePickerTypeDateTime = "DateTimePickerTypeDateTime" 12 | DatePickerTypeDate = "DatePickerTypeDate" 13 | RangeDateTimePickerTypeArrayDateTime = "RangeDateTimePickerTypeArrayDateTime" 14 | RangeDatePickerTypeArrayDate = "RangeDatePickerTypeArrayDate" 15 | CheckboxTypeBoolean = "CheckboxTypeBoolean" 16 | ) 17 | 18 | const ( 19 | FieldTypeBool = "Bool" 20 | FieldTypeCoordinates = "Coordinates" 21 | FieldTypeDateTime = "DateTime" 22 | ) 23 | -------------------------------------------------------------------------------- /service/component/ddos/ddos.go: -------------------------------------------------------------------------------- 1 | package ddos 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | 7 | "github.com/latolukasz/beeorm/v2" 8 | ) 9 | 10 | type IDDOS interface { 11 | ProtectManyAttempts(redis beeorm.RedisCache, protectCriterion string, maxAttempts int, ttl int) bool 12 | } 13 | 14 | type DDOS struct { 15 | } 16 | 17 | func (t *DDOS) ProtectManyAttempts(redis beeorm.RedisCache, protectCriterion string, maxAttempts int, ttl int) bool { 18 | attempts, has := redis.Get("ddos_" + protectCriterion) 19 | count := 0 20 | 21 | if len(attempts) > 0 { 22 | var err error 23 | 24 | count, err = strconv.Atoi(attempts) 25 | if err != nil { 26 | panic(err) 27 | } 28 | } 29 | 30 | if has && count >= maxAttempts { 31 | return false 32 | } 33 | 34 | redis.Set("ddos_"+protectCriterion, count+1, time.Second*time.Duration(ttl)) 35 | 36 | return true 37 | } 38 | -------------------------------------------------------------------------------- /service/component/dynamic_link/dynamic_link.go: -------------------------------------------------------------------------------- 1 | package dynamiclink 2 | 3 | type IGenerator interface { 4 | GenerateDynamicLink(string) (*GenerateResponse, error) 5 | } 6 | 7 | type GenerateResponse struct { 8 | Link string 9 | PreviewLink string 10 | } 11 | -------------------------------------------------------------------------------- /service/component/dynamic_link/mocks/dynamic_link.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | 6 | dynamiclink "github.com/coretrix/hitrix/service/component/dynamic_link" 7 | ) 8 | 9 | type FakeDynamicLinksGenerator struct { 10 | mock.Mock 11 | } 12 | 13 | func (f *FakeDynamicLinksGenerator) GenerateDynamicLink(s string) (*dynamiclink.GenerateResponse, error) { 14 | args := f.Called(s) 15 | 16 | return args.Get(0).(*dynamiclink.GenerateResponse), args.Error(1) 17 | } 18 | -------------------------------------------------------------------------------- /service/component/elorus/elorus_provider.go: -------------------------------------------------------------------------------- 1 | package elorus 2 | 3 | import "io" 4 | 5 | type IProvider interface { 6 | CreateContact(request *CreateContactRequest) (*Response, error) 7 | CreateInvoice(request *CreateInvoiceRequest) (*Response, error) 8 | GetInvoiceList(request *GetInvoiceListRequest) (*InvoiceListResponse, error) 9 | DownloadInvoice(request *DownloadInvoiceRequest) (*io.ReadCloser, error) 10 | } 11 | -------------------------------------------------------------------------------- /service/component/elorus/mocks/elorus.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/stretchr/testify/mock" 7 | 8 | "github.com/coretrix/hitrix/service/component/elorus" 9 | ) 10 | 11 | type FakeElorus struct { 12 | mock.Mock 13 | } 14 | 15 | func (e *FakeElorus) CreateContact(request *elorus.CreateContactRequest) (*elorus.Response, error) { 16 | args := e.Called(request) 17 | 18 | return args.Get(0).(*elorus.Response), args.Error(1) 19 | } 20 | 21 | func (e *FakeElorus) CreateInvoice(request *elorus.CreateInvoiceRequest) (*elorus.Response, error) { 22 | args := e.Called(request) 23 | 24 | return args.Get(0).(*elorus.Response), args.Error(1) 25 | } 26 | 27 | func (e *FakeElorus) GetInvoiceList(request *elorus.GetInvoiceListRequest) (*elorus.InvoiceListResponse, error) { 28 | args := e.Called(request) 29 | 30 | return args.Get(0).(*elorus.InvoiceListResponse), args.Error(1) 31 | } 32 | 33 | func (e *FakeElorus) DownloadInvoice(request *elorus.DownloadInvoiceRequest) (*io.ReadCloser, error) { 34 | args := e.Called(request) 35 | 36 | return args.Get(0).(*io.ReadCloser), args.Error(1) 37 | } 38 | -------------------------------------------------------------------------------- /service/component/exporter/mocks/exporter.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/stretchr/testify/mock" 4 | 5 | type FakeExporter struct { 6 | mock.Mock 7 | } 8 | 9 | func (e *FakeExporter) XLSXExportToFile(sheet string, columns []string, rows [][]interface{}, filePath string) error { 10 | return e.Called(sheet, columns, rows, filePath).Get(0).(error) 11 | } 12 | 13 | func (e *FakeExporter) XLSXExportToByte(sheet string, columns []string, rows [][]interface{}) ([]byte, error) { 14 | args := e.Called(sheet, columns, rows) 15 | 16 | return args.Get(0).([]byte), args.Error(1) 17 | } 18 | 19 | func (e *FakeExporter) CSVExportToFile(columns []string, rows [][]interface{}, filePath string) error { 20 | return e.Called(columns, rows, filePath).Get(0).(error) 21 | } 22 | 23 | func (e *FakeExporter) CSVExportToByte(columns []string, rows [][]interface{}) ([]byte, error) { 24 | args := e.Called(columns, rows) 25 | 26 | return args.Get(0).([]byte), args.Error(1) 27 | } 28 | -------------------------------------------------------------------------------- /service/component/fcm/fcm.go: -------------------------------------------------------------------------------- 1 | package fcm 2 | 3 | import ( 4 | "context" 5 | 6 | firebase "firebase.google.com/go" 7 | "firebase.google.com/go/messaging" 8 | ) 9 | 10 | type FCM interface { 11 | Send(ctx context.Context, message *messaging.Message) (string, error) 12 | SendDryRun(ctx context.Context, message *messaging.Message) (string, error) 13 | SendAll(ctx context.Context, messages []*messaging.Message) (*messaging.BatchResponse, error) 14 | SendAllDryRun(ctx context.Context, messages []*messaging.Message) (*messaging.BatchResponse, error) 15 | SendMulticast(ctx context.Context, message *messaging.MulticastMessage) (*messaging.BatchResponse, error) 16 | SendMulticastDryRun(ctx context.Context, message *messaging.MulticastMessage) (*messaging.BatchResponse, error) 17 | SubscribeToTopic(ctx context.Context, tokens []string, topic string) (*messaging.TopicManagementResponse, error) 18 | UnsubscribeFromTopic(ctx context.Context, tokens []string, topic string) (*messaging.TopicManagementResponse, error) 19 | } 20 | 21 | func NewFCM(ctx context.Context) (FCM, error) { 22 | app, err := firebase.NewApp(ctx, nil) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return app.Messaging(ctx) 28 | } 29 | -------------------------------------------------------------------------------- /service/component/feature_flag/feature_flag.go: -------------------------------------------------------------------------------- 1 | package featureflag 2 | 3 | import ( 4 | "github.com/coretrix/hitrix/datalayer" 5 | "github.com/coretrix/hitrix/service/component/app" 6 | "github.com/coretrix/hitrix/service/component/clock" 7 | ) 8 | 9 | type ServiceFeatureFlagInterface interface { 10 | IsActive(ormService *datalayer.ORM, name string) bool 11 | FailIfIsNotActive(ormService *datalayer.ORM, name string) error 12 | Enable(ormService *datalayer.ORM, name string) error 13 | Disable(ormService *datalayer.ORM, name string) error 14 | GetScriptsSingleInstance(ormService *datalayer.ORM) []app.IScript 15 | GetScriptsMultiInstance(ormService *datalayer.ORM) []app.IScript 16 | Register(featureFlags ...IFeatureFlag) 17 | Sync(ormService *datalayer.ORM, clockService clock.IClock) 18 | } 19 | -------------------------------------------------------------------------------- /service/component/generator/generator.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "github.com/AmirSoleimani/VoucherCodeGenerator/vcgen" 5 | ) 6 | 7 | type IGenerator interface { 8 | GenerateRandomRangeNumber(int64, int64) int64 9 | GenerateSha256Hash(string) string 10 | GenerateRandomCode(*vcgen.Generator) string 11 | RandomPasswordGenerator(int) string 12 | RandomPINCodeGenerator(int) string 13 | } 14 | -------------------------------------------------------------------------------- /service/component/generator/mocks/generator.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/AmirSoleimani/VoucherCodeGenerator/vcgen" 5 | "github.com/stretchr/testify/mock" 6 | ) 7 | 8 | type FakeGenerator struct { 9 | mock.Mock 10 | } 11 | 12 | func (f *FakeGenerator) GenerateRandomRangeNumber(min, max int64) int64 { 13 | return int64(f.Called(min, max).Int(0)) 14 | } 15 | 16 | func (f *FakeGenerator) GenerateSha256Hash(input string) string { 17 | return f.Called(input).String(0) 18 | } 19 | 20 | func (f *FakeGenerator) GenerateRandomCode(generator *vcgen.Generator) string { 21 | return f.Called(generator).String(0) 22 | } 23 | 24 | func (f *FakeGenerator) RandomPasswordGenerator(l int) string { 25 | return f.Called(l).String(0) 26 | } 27 | func (f *FakeGenerator) RandomPINCodeGenerator(l int) string { 28 | return f.Called(l).String(0) 29 | } 30 | -------------------------------------------------------------------------------- /service/component/generator/simple_test.go: -------------------------------------------------------------------------------- 1 | package generator_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/coretrix/hitrix/service/component/generator" 9 | ) 10 | 11 | func TestGenerateRandomRangeNumber(t *testing.T) { 12 | generatorService := &generator.SimpleGenerator{} 13 | 14 | code := generatorService.GenerateRandomRangeNumber(1000, 9999) 15 | 16 | assert.True(t, code >= 1000 && code <= 9999) 17 | } 18 | -------------------------------------------------------------------------------- /service/component/geocoding/language_constants.go: -------------------------------------------------------------------------------- 1 | package geocoding 2 | 3 | import "github.com/coretrix/hitrix/pkg/entity" 4 | 5 | const ( 6 | LanguageEnglish = Language("en") 7 | LanguageBulgarian = Language("bg") 8 | ) 9 | 10 | type Language string 11 | 12 | var languageToEnumMapping = map[Language]string{ 13 | LanguageEnglish: entity.LanguageEnglish, 14 | LanguageBulgarian: entity.LanguageBulgarian, 15 | } 16 | -------------------------------------------------------------------------------- /service/component/geocoding/mocks/geocoding.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/stretchr/testify/mock" 10 | 11 | "github.com/coretrix/hitrix/datalayer" 12 | "github.com/coretrix/hitrix/service/component/geocoding" 13 | ) 14 | 15 | type FakeGeocoding struct { 16 | mock.Mock 17 | } 18 | 19 | func (f *FakeGeocoding) Geocode(_ context.Context, _ *datalayer.ORM, address string, language geocoding.Language) (*geocoding.Address, error) { 20 | args := f.Called(address, language) 21 | 22 | return args.Get(0).(*geocoding.Address), args.Error(1) 23 | } 24 | 25 | func (f *FakeGeocoding) ReverseGeocode( 26 | _ context.Context, 27 | _ *datalayer.ORM, 28 | latLng *geocoding.LatLng, 29 | language geocoding.Language, 30 | ) (*geocoding.Address, error) { 31 | args := f.Called(latLng, language) 32 | 33 | return args.Get(0).(*geocoding.Address), args.Error(1) 34 | } 35 | 36 | func (f *FakeGeocoding) CutCoordinates(float float64, precision int) (float64, error) { 37 | asString := fmt.Sprintf("%.8f", float) 38 | asStringParts := strings.Split(asString, ".") 39 | 40 | return strconv.ParseFloat(asString[0:len(asStringParts[0])+1+precision], 64) 41 | } 42 | -------------------------------------------------------------------------------- /service/component/geocoding/provider.go: -------------------------------------------------------------------------------- 1 | package geocoding 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type Provider interface { 8 | Geocode(ctx context.Context, address string, language Language) (*Address, interface{}, error) 9 | ReverseGeocode(ctx context.Context, latLng *LatLng, language Language) (*Address, interface{}, error) 10 | GetName() string 11 | } 12 | -------------------------------------------------------------------------------- /service/component/google_analytics/google_analytics_provider.go: -------------------------------------------------------------------------------- 1 | package googleanalytics 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | ga "google.golang.org/api/analyticsdata/v1beta" 6 | ) 7 | 8 | type Provider string 9 | 10 | const ( 11 | UA Provider = "ua" // TODO: Implement later 12 | GA4 Provider = "ga4" 13 | ) 14 | 15 | func (p Provider) String() string { 16 | return string(p) 17 | } 18 | 19 | type IProvider interface { 20 | GetName() Provider 21 | RunReport(ctx context.Context, runReportRequest *ga.RunReportRequest) (*ga.RunReportResponse, error) 22 | GetDimensionsAndMetrics(ctx context.Context) ([]*ga.DimensionMetadata, []*ga.MetricMetadata, error) 23 | GetMetrics(ctx context.Context, dateFrom, dateTo string, metrics []string, dimensions []string) (map[uint64]map[string]interface{}, error) 24 | } 25 | -------------------------------------------------------------------------------- /service/component/google_analytics/mocks/ga4.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stretchr/testify/mock" 7 | ga "google.golang.org/api/analyticsdata/v1beta" 8 | 9 | googleanalytics "github.com/coretrix/hitrix/service/component/google_analytics" 10 | ) 11 | 12 | type FakeGoogleAnalytics4 struct { 13 | mock.Mock 14 | } 15 | 16 | func (f *FakeGoogleAnalytics4) GetName() googleanalytics.Provider { 17 | return f.Called().Get(0).(googleanalytics.Provider) 18 | } 19 | 20 | func (f *FakeGoogleAnalytics4) RunReport(ctx context.Context, runReportRequest *ga.RunReportRequest) (*ga.RunReportResponse, error) { 21 | args := f.Called(ctx, runReportRequest) 22 | 23 | return args.Get(0).(*ga.RunReportResponse), args.Error(1) 24 | } 25 | 26 | func (f *FakeGoogleAnalytics4) GetDimensionsAndMetrics(ctx context.Context) ([]*ga.DimensionMetadata, []*ga.MetricMetadata, error) { 27 | args := f.Called(ctx) 28 | 29 | return args.Get(0).([]*ga.DimensionMetadata), args.Get(1).([]*ga.MetricMetadata), args.Error(2) 30 | } 31 | 32 | func (f *FakeGoogleAnalytics4) GetMetrics( 33 | _ context.Context, 34 | dateFrom string, 35 | dateTo string, 36 | metrics []string, 37 | dimensions []string, 38 | ) (map[uint64]map[string]interface{}, error) { 39 | args := f.Called(dateTo, dateFrom, metrics, dimensions) 40 | 41 | return args.Get(0).(map[uint64]map[string]interface{}), args.Error(1) 42 | } 43 | -------------------------------------------------------------------------------- /service/component/google_analytics/mocks/google_analytics.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | 6 | googleanalytics "github.com/coretrix/hitrix/service/component/google_analytics" 7 | ) 8 | 9 | type FakeGoogleAnalytics struct { 10 | mock.Mock 11 | } 12 | 13 | func (f *FakeGoogleAnalytics) GetProvider(_ googleanalytics.Provider) googleanalytics.IProvider { 14 | return f.Called().Get(0).(googleanalytics.IProvider) 15 | } 16 | -------------------------------------------------------------------------------- /service/component/goroutine/goroutine.go: -------------------------------------------------------------------------------- 1 | package goroutine 2 | 3 | type IGoroutine interface { 4 | Goroutine(fn func()) 5 | GoroutineWithRestart(fn func()) 6 | } 7 | -------------------------------------------------------------------------------- /service/component/goroutine/manager.go: -------------------------------------------------------------------------------- 1 | package goroutine 2 | 3 | import ( 4 | "time" 5 | 6 | errorlogger "github.com/coretrix/hitrix/service/component/error_logger" 7 | ) 8 | 9 | type Manager struct { 10 | ErrorLogger errorlogger.ErrorLogger 11 | } 12 | 13 | func NewGoroutineManager(errorLoggerService errorlogger.ErrorLogger) IGoroutine { 14 | return &Manager{ErrorLogger: errorLoggerService} 15 | } 16 | 17 | func (g *Manager) Goroutine(fn func()) { 18 | go g.routine(fn, false) 19 | } 20 | 21 | func (g *Manager) GoroutineWithRestart(fn func()) { 22 | go g.routine(fn, true) 23 | } 24 | 25 | func (g *Manager) routine(fn func(), autoRestart bool) { 26 | defer func() { 27 | if r := recover(); r != nil { 28 | g.ErrorLogger.LogError(r) 29 | 30 | if autoRestart { 31 | time.Sleep(time.Second) 32 | 33 | go g.routine(fn, true) 34 | } 35 | } 36 | }() 37 | 38 | fn() 39 | } 40 | -------------------------------------------------------------------------------- /service/component/html2pdf/mocks/html2pdf.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | ) 6 | 7 | type HTML2PDF struct { 8 | mock.Mock 9 | } 10 | 11 | func (t *HTML2PDF) HTMLToPdf(_ string) []byte { 12 | args := t.Called() 13 | 14 | return args.Get(0).([]byte) 15 | } 16 | -------------------------------------------------------------------------------- /service/component/instagram/mocks/fakeRapidAPIInstagram.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | 6 | "github.com/coretrix/hitrix/service/component/instagram" 7 | ) 8 | 9 | type FakeRapidAPIInstagram struct { 10 | mock.Mock 11 | } 12 | 13 | func (f *FakeRapidAPIInstagram) GetName() string { 14 | return f.Called().Get(0).(string) 15 | } 16 | 17 | func (f *FakeRapidAPIInstagram) GetAccount(_ string) (*instagram.Account, error) { 18 | args := f.Called() 19 | 20 | return args.Get(0).(*instagram.Account), args.Error(1) 21 | } 22 | 23 | func (f *FakeRapidAPIInstagram) GetFeed(_ int64, _ string) ([]*instagram.Post, string, error) { 24 | args := f.Called() 25 | 26 | return args.Get(0).([]*instagram.Post), args.String(1), args.Error(2) 27 | } 28 | -------------------------------------------------------------------------------- /service/component/instagram/mocks/instagram.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | 6 | "github.com/coretrix/hitrix/service/component/instagram" 7 | ) 8 | 9 | type FakeInstagram struct { 10 | mock.Mock 11 | } 12 | 13 | func (f *FakeInstagram) GetRandomProvider() instagram.IProvider { 14 | return f.Called().Get(0).(instagram.IProvider) 15 | } 16 | 17 | func (f *FakeInstagram) GetProvider(_ string) instagram.IProvider { 18 | return f.Called().Get(0).(instagram.IProvider) 19 | } 20 | -------------------------------------------------------------------------------- /service/component/kubernetes/kubernetes_provider.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import "context" 4 | 5 | type IProvider interface { 6 | GetIngressDomains(ctx context.Context) ([]string, error) 7 | AddIngress(ctx context.Context, domain, secretName, serviceName, servicePortName string, annotations map[string]string) error 8 | RemoveIngress(ctx context.Context, domain string) error 9 | IsCertificateProvisioned(ctx context.Context, secretName string) (bool, error) 10 | } 11 | -------------------------------------------------------------------------------- /service/component/kubernetes/mocks/kubernetes.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stretchr/testify/mock" 7 | ) 8 | 9 | type FakeKubernetes struct { 10 | mock.Mock 11 | } 12 | 13 | func (k *FakeKubernetes) GetIngressDomains(ctx context.Context) ([]string, error) { 14 | args := k.Called(ctx) 15 | 16 | return args.Get(0).([]string), args.Error(1) 17 | } 18 | 19 | func (k *FakeKubernetes) AddIngress( 20 | ctx context.Context, 21 | domain string, 22 | secretName string, 23 | serviceName string, 24 | servicePortName string, 25 | _ map[string]string, 26 | ) error { 27 | args := k.Called(ctx, domain, secretName, serviceName, servicePortName) 28 | 29 | return args.Error(0) 30 | } 31 | 32 | func (k *FakeKubernetes) RemoveIngress(ctx context.Context, domain string) error { 33 | args := k.Called(ctx, domain) 34 | 35 | return args.Error(0) 36 | } 37 | 38 | func (k *FakeKubernetes) IsCertificateProvisioned(ctx context.Context, secretName string) (bool, error) { 39 | args := k.Called(ctx, secretName) 40 | 41 | return args.Get(0).(bool), args.Error(1) 42 | } 43 | -------------------------------------------------------------------------------- /service/component/license_plate_recognizer/license_plate_recognizer.go: -------------------------------------------------------------------------------- 1 | package licenseplaterecognizer 2 | 3 | type LicensePlateRecognizer interface { 4 | RecognizeFromImage(base64image string) ([]string, error) 5 | } 6 | -------------------------------------------------------------------------------- /service/component/license_plate_recognizer/mocks/license_plate_recognizer.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/stretchr/testify/mock" 4 | 5 | type FakeLicensePlateRecognizer struct { 6 | mock.Mock 7 | } 8 | 9 | func (f *FakeLicensePlateRecognizer) RecognizeFromImage(base64image string) ([]string, error) { 10 | args := f.Called(base64image) 11 | 12 | return args.Get(0).([]string), args.Error(1) 13 | } 14 | -------------------------------------------------------------------------------- /service/component/license_plate_recognizer/moskvich.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coretrix/hitrix/3740ffa9352d5b402f4da0bb56ddca25b9aab8ae/service/component/license_plate_recognizer/moskvich.jpg -------------------------------------------------------------------------------- /service/component/license_plate_recognizer/platerecognizer_test.go: -------------------------------------------------------------------------------- 1 | package licenseplaterecognizer 2 | 3 | import ( 4 | "bufio" 5 | "encoding/base64" 6 | "io" 7 | "os" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestPlateRecognizerRecognizeFromImage(t *testing.T) { 14 | f, _ := os.Open("moskvich.jpg") 15 | reader := bufio.NewReader(f) 16 | content, _ := io.ReadAll(reader) 17 | 18 | encoded := base64.StdEncoding.EncodeToString(content) 19 | 20 | want := []string{"BP9716CX"} 21 | 22 | plateRecognizer := NewPlateRecognizer("7e1e7d418814c67da456418f87e66e03d7591b49") 23 | got, err := plateRecognizer.RecognizeFromImage(encoded) 24 | 25 | assert.Nil(t, err) 26 | assert.Equal(t, want, got) 27 | } 28 | -------------------------------------------------------------------------------- /service/component/localize/source.go: -------------------------------------------------------------------------------- 1 | package localize 2 | 3 | type Source interface { 4 | Push([]string) error 5 | Pull() (map[string]string, error) 6 | } 7 | -------------------------------------------------------------------------------- /service/component/mail/mocks/sender.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | 6 | "github.com/coretrix/hitrix/datalayer" 7 | "github.com/coretrix/hitrix/service/component/mail" 8 | ) 9 | 10 | type Sender struct { 11 | mock.Mock 12 | } 13 | 14 | func (m *Sender) GetTemplateKeyFromConfig(templateName string) (string, error) { 15 | args := m.Called(templateName) 16 | 17 | return args.Get(0).(string), args.Error(1) 18 | } 19 | func (m *Sender) SendTemplate(_ *datalayer.ORM, message *mail.Message) error { 20 | return m.Called(message.To).Error(0) 21 | } 22 | 23 | func (m *Sender) SendTemplateWithAttachments(_ *datalayer.ORM, message *mail.MessageAttachment) error { 24 | return m.Called(message.To).Error(0) 25 | } 26 | 27 | func (m *Sender) GetTemplateHTMLCode(templateName string) (string, error) { 28 | args := m.Called(templateName) 29 | 30 | return args.Get(0).(string), args.Error(1) 31 | } 32 | -------------------------------------------------------------------------------- /service/component/mail/provider.go: -------------------------------------------------------------------------------- 1 | package mail 2 | 3 | import ( 4 | "github.com/coretrix/hitrix/service/component/config" 5 | ) 6 | 7 | type NewSenderFunc func(configService config.IConfig) (IProvider, error) 8 | 9 | type IProvider interface { 10 | GetTemplateKeyFromConfig(configService config.IConfig, templateName string) (string, error) 11 | SendTemplate(message *Message) error 12 | GetDefaultFromEmail() string 13 | GetDefaultFromName() string 14 | SendTemplateWithAttachments(message *MessageAttachment) error 15 | GetTemplateHTMLCode(templateName string) (string, error) 16 | } 17 | -------------------------------------------------------------------------------- /service/component/otp/mocks/gateway.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | 6 | "github.com/coretrix/hitrix/service/component/otp" 7 | ) 8 | 9 | type FakeGateway struct { 10 | mock.Mock 11 | } 12 | 13 | func (f *FakeGateway) GetName() string { 14 | return f.Called().String(0) 15 | } 16 | 17 | func (f *FakeGateway) GetCode() string { 18 | return f.Called().String(0) 19 | } 20 | 21 | func (f *FakeGateway) GetPhonePrefixes() []string { 22 | return f.Called().Get(0).([]string) 23 | } 24 | 25 | func (f *FakeGateway) SendOTP(phone *otp.Phone, code string) (string, string, error) { 26 | args := f.Called(phone, code) 27 | 28 | return args.String(0), args.String(1), args.Error(2) 29 | } 30 | 31 | func (f *FakeGateway) Call(phone *otp.Phone, code string, customMessage string) (string, string, error) { 32 | args := f.Called(phone, code, customMessage) 33 | 34 | return args.String(0), args.String(1), args.Error(2) 35 | } 36 | 37 | func (f *FakeGateway) VerifyOTP(phone *otp.Phone, code, generatedCode string) (string, string, bool, bool, error) { 38 | args := f.Called(phone, code, generatedCode) 39 | 40 | return args.String(0), args.String(1), args.Bool(2), args.Bool(3), args.Error(4) 41 | } 42 | -------------------------------------------------------------------------------- /service/component/otp/sms_provider.go: -------------------------------------------------------------------------------- 1 | package otp 2 | 3 | type IOTPSMSGateway interface { 4 | GetName() string 5 | GetCode() string 6 | GetPhonePrefixes() []string 7 | SendOTP(phone *Phone, code string) (string, string, error) 8 | Call(phone *Phone, code string, customMessage string) (string, string, error) 9 | VerifyOTP(phone *Phone, code, generatedCode string) (string, string, bool, bool, error) 10 | } 11 | -------------------------------------------------------------------------------- /service/component/password/password.go: -------------------------------------------------------------------------------- 1 | package password 2 | 3 | import ( 4 | "github.com/coretrix/hitrix/service/component/config" 5 | ) 6 | 7 | type NewPasswordManagerFunc func(configService config.IConfig) IPassword 8 | 9 | type IPassword interface { 10 | VerifyPassword(password string, hash string) bool 11 | HashPassword(password string) (string, error) 12 | } 13 | -------------------------------------------------------------------------------- /service/component/password/simple.go: -------------------------------------------------------------------------------- 1 | package password 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/base64" 6 | 7 | "github.com/coretrix/hitrix/service/component/config" 8 | ) 9 | 10 | type SimpleManager struct { 11 | } 12 | 13 | func NewSimpleManager(_ config.IConfig) IPassword { 14 | return &SimpleManager{} 15 | } 16 | 17 | func (p *SimpleManager) VerifyPassword(password string, hash string) bool { 18 | passwordHash, err := p.HashPassword(password) 19 | 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | return passwordHash == hash 25 | } 26 | 27 | func (p *SimpleManager) HashPassword(password string) (string, error) { 28 | sha256Hash := sha256.New() 29 | _, err := sha256Hash.Write([]byte(password)) 30 | 31 | if err != nil { 32 | return "", err 33 | } 34 | 35 | return base64.StdEncoding.EncodeToString(sha256Hash.Sum(nil)), nil 36 | } 37 | -------------------------------------------------------------------------------- /service/component/password/simple_test.go: -------------------------------------------------------------------------------- 1 | package password_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/coretrix/hitrix/service/component/password" 9 | ) 10 | 11 | func TestHashPasswordTrue(t *testing.T) { 12 | passwordService := &password.SimpleManager{} 13 | 14 | hash, err := passwordService.HashPassword("Str0NGPa$$W0rD!") 15 | 16 | assert.NoError(t, err, "Cannot create hash") 17 | assert.Equal(t, "eh71ZMSd5oCpYTaazon8jc53bo0sMiWSPmPVuMVB9mU=", hash, "Hash is not valid") 18 | } 19 | 20 | func TestHashPasswordFalse(t *testing.T) { 21 | passwordService := &password.SimpleManager{} 22 | 23 | hash, err := passwordService.HashPassword("Str0NGPa$$W0rD!1") 24 | 25 | assert.NoError(t, err, "Cannot create hash") 26 | assert.NotEqual(t, "eh71ZMSd5oCpYTaazon8jc53bo0sMiWSPmPVuMVB9mU=", hash) 27 | } 28 | 29 | func TestVerifyPasswordTrue(t *testing.T) { 30 | passwordService := &password.SimpleManager{} 31 | 32 | assert.True(t, passwordService.VerifyPassword("Str0NGPa$$W0rD!", "eh71ZMSd5oCpYTaazon8jc53bo0sMiWSPmPVuMVB9mU=")) 33 | } 34 | 35 | func TestVerifyPasswordFalse(t *testing.T) { 36 | passwordService := &password.SimpleManager{} 37 | 38 | assert.False(t, passwordService.VerifyPassword("Str0NGPa$$W0rD!", "")) 39 | } 40 | -------------------------------------------------------------------------------- /service/component/request_logger/request_logger.go: -------------------------------------------------------------------------------- 1 | package requestlogger 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/coretrix/hitrix/datalayer" 7 | "github.com/coretrix/hitrix/pkg/entity" 8 | ) 9 | 10 | const ID = "request_logger_id" 11 | 12 | type IRequestLogger interface { 13 | LogRequest(ormService *datalayer.ORM, appName, url string, request *http.Request, contentType string) *entity.RequestLoggerEntity 14 | LogResponse(ormService *datalayer.ORM, requestLoggerEntity *entity.RequestLoggerEntity, responseBody []byte, status int) 15 | } 16 | -------------------------------------------------------------------------------- /service/component/sentry/mocks/sentry.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/getsentry/sentry-go" 8 | "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | type FakeSentry struct { 12 | mock.Mock 13 | } 14 | 15 | func (f *FakeSentry) CaptureMessage(message string) { 16 | f.Called(message) 17 | } 18 | 19 | func (f *FakeSentry) CaptureException(exception error) { 20 | f.Called(exception) 21 | } 22 | 23 | func (f *FakeSentry) Flush(timeout time.Duration) { 24 | f.Called(timeout) 25 | } 26 | 27 | func (f *FakeSentry) StartSpan(_ context.Context, operation string, options ...sentry.SpanOption) *sentry.Span { 28 | args := f.Called(operation, options) 29 | 30 | return args.Get(0).(*sentry.Span) 31 | } 32 | -------------------------------------------------------------------------------- /service/component/sentry/sentry.go: -------------------------------------------------------------------------------- 1 | package sentry 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/getsentry/sentry-go" 8 | ) 9 | 10 | type ISentry interface { 11 | CaptureMessage(message string) 12 | CaptureException(exception error) 13 | Flush(timeout time.Duration) 14 | StartSpan(ctx context.Context, operation string, options ...sentry.SpanOption) *sentry.Span 15 | } 16 | 17 | type v struct { 18 | } 19 | 20 | func Init(dsn, mode, release string, tracesSampleRate *float64) ISentry { 21 | tracesSampleRateValue := 0.0 22 | if tracesSampleRate != nil { 23 | tracesSampleRateValue = *tracesSampleRate 24 | } 25 | 26 | err := sentry.Init(sentry.ClientOptions{ 27 | Dsn: dsn, 28 | TracesSampleRate: tracesSampleRateValue, 29 | Release: release, 30 | Environment: mode, 31 | }) 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | return &v{} 37 | } 38 | 39 | func (v *v) CaptureMessage(message string) { 40 | sentry.CaptureMessage(message) 41 | } 42 | 43 | func (v *v) CaptureException(exception error) { 44 | sentry.CaptureException(exception) 45 | } 46 | 47 | func (v *v) Flush(timeout time.Duration) { 48 | sentry.Flush(timeout) 49 | } 50 | 51 | func (v *v) StartSpan(ctx context.Context, operation string, options ...sentry.SpanOption) *sentry.Span { 52 | return sentry.StartSpan(ctx, operation, options...) 53 | } 54 | -------------------------------------------------------------------------------- /service/component/setting/setting.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "github.com/coretrix/hitrix/datalayer" 5 | "github.com/coretrix/hitrix/pkg/entity" 6 | ) 7 | 8 | type ServiceSettingInterface interface { 9 | Get(ormService *datalayer.ORM, key string) (*entity.SettingsEntity, bool) 10 | GetString(ormService *datalayer.ORM, key string) (string, bool) 11 | GetInt(ormService *datalayer.ORM, key string) (int, bool) 12 | GetUint(ormService *datalayer.ORM, key string) (uint, bool) 13 | GetInt64(ormService *datalayer.ORM, key string) (int64, bool) 14 | GetUint64(ormService *datalayer.ORM, key string) (uint64, bool) 15 | GetFloat64(ormService *datalayer.ORM, key string) (float64, bool) 16 | GetBool(ormService *datalayer.ORM, key string) (bool, bool) 17 | } 18 | -------------------------------------------------------------------------------- /service/component/slack/slack.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "github.com/slack-go/slack" 5 | ) 6 | 7 | type Slack interface { 8 | GetDevPanelURL() string 9 | GetErrorChannel() string 10 | SendToChannel(botName, channelName, message string, opt ...slack.MsgOption) error 11 | } 12 | 13 | //TODO refactor 14 | -------------------------------------------------------------------------------- /service/component/slack/slack_go.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/slack-go/slack" 7 | ) 8 | 9 | type APIClient struct { 10 | clients map[string]*slack.Client 11 | errorChannel string 12 | devPanelURL string 13 | } 14 | 15 | func NewSlackGo(botTokens map[string]string, errorChannel, devPanelURL string) *APIClient { 16 | clients := make(map[string]*slack.Client) 17 | for name, token := range botTokens { 18 | clients[name] = slack.New(token) 19 | } 20 | 21 | return &APIClient{clients: clients, errorChannel: errorChannel, devPanelURL: devPanelURL} 22 | } 23 | 24 | func (s *APIClient) GetDevPanelURL() string { 25 | return s.devPanelURL 26 | } 27 | 28 | func (s *APIClient) GetErrorChannel() string { 29 | return s.errorChannel 30 | } 31 | 32 | func (s *APIClient) SendToChannel(botName, channelName, message string, opt ...slack.MsgOption) error { 33 | client, ok := s.clients[botName] 34 | if !ok { 35 | return fmt.Errorf(`bot "%s" not defined`, botName) 36 | } 37 | 38 | opt = append(opt, slack.MsgOptionText(message, true)) 39 | _, _, err := client.PostMessage(channelName, opt...) 40 | 41 | return err 42 | } 43 | -------------------------------------------------------------------------------- /service/component/sms/kavenegar_provider.go: -------------------------------------------------------------------------------- 1 | package sms 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/kavenegar/kavenegar-go" 8 | 9 | "github.com/coretrix/hitrix/service/component/clock" 10 | "github.com/coretrix/hitrix/service/component/config" 11 | ) 12 | 13 | const Kavenegar = "kavenegar" 14 | 15 | type KavenegarProvider struct { 16 | APIKey string 17 | Sender string 18 | } 19 | 20 | func NewKavenegarProvider(configService config.IConfig, _ clock.IClock) (IProvider, error) { 21 | // register kavenegar 22 | apiKey, ok := configService.String("sms.kavenegar.api_key") 23 | 24 | if !ok { 25 | return nil, errors.New("missing sms.kavenegar.api_key") 26 | } 27 | 28 | sender, ok := configService.String("sms.kavenegar.sender") 29 | 30 | if !ok { 31 | return nil, errors.New("missing sms.kavenegar.sender") 32 | } 33 | 34 | return &KavenegarProvider{ 35 | APIKey: apiKey, 36 | Sender: sender, 37 | }, nil 38 | } 39 | 40 | func (g *KavenegarProvider) GetName() string { 41 | return Kavenegar 42 | } 43 | 44 | func (g *KavenegarProvider) SendSMSMessage(message *Message) (string, error) { 45 | api := kavenegar.New(g.APIKey) 46 | 47 | res, err := api.Message.Send(g.Sender, []string{message.Number}, message.Text, nil) 48 | if err != nil { 49 | return err.Error(), err 50 | } 51 | 52 | if len(res) < 1 { 53 | e := fmt.Errorf("there was a problem sending sms") 54 | 55 | return e.Error(), e 56 | } 57 | 58 | return res[0].StatusText, nil 59 | } 60 | -------------------------------------------------------------------------------- /service/component/sms/log.go: -------------------------------------------------------------------------------- 1 | package sms 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | 8 | "github.com/coretrix/hitrix/datalayer" 9 | ) 10 | 11 | type LogEntity interface { 12 | beeorm.Entity 13 | SetStatus(string) 14 | SetTo(string) 15 | SetText(string) 16 | SetFromPrimaryProvider(string) 17 | SetFromSecondaryProvider(string) 18 | SetPrimaryProviderError(string) 19 | SetSecondaryProviderError(string) 20 | SetType(string) 21 | SetSentAt(time time.Time) 22 | } 23 | 24 | type DBLog struct { 25 | ormService *datalayer.ORM 26 | logEntity LogEntity 27 | } 28 | 29 | func (db *DBLog) Do() { 30 | db.ormService.Flush(db.logEntity) 31 | } 32 | 33 | type Logger interface { 34 | Do() 35 | } 36 | 37 | func NewSmsLog(ormService *datalayer.ORM, entity LogEntity) Logger { 38 | return &DBLog{ormService: ormService, logEntity: entity} 39 | } 40 | -------------------------------------------------------------------------------- /service/component/sms/mocks/sms.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | 6 | "github.com/coretrix/hitrix/datalayer" 7 | "github.com/coretrix/hitrix/service/component/sms" 8 | ) 9 | 10 | type FakeSMSSender struct { 11 | mock.Mock 12 | } 13 | 14 | func (f *FakeSMSSender) SendMessage(_ *datalayer.ORM, message *sms.Message) error { 15 | return f.Called(message).Error(0) 16 | } 17 | -------------------------------------------------------------------------------- /service/component/sms/provider.go: -------------------------------------------------------------------------------- 1 | package sms 2 | 3 | import ( 4 | "github.com/coretrix/hitrix/service/component/clock" 5 | "github.com/coretrix/hitrix/service/component/config" 6 | ) 7 | 8 | const ( 9 | success = "sent successfully" 10 | failure = "sent unsuccessfully" 11 | 12 | timeoutInSeconds = 5 13 | ) 14 | 15 | type NewProviderFunc func(configService config.IConfig, clockService clock.IClock) (IProvider, error) 16 | 17 | type IProvider interface { 18 | SendSMSMessage(msg *Message) (string, error) 19 | GetName() string 20 | } 21 | 22 | type Message struct { 23 | Text string 24 | Number string 25 | Provider *Provider 26 | } 27 | 28 | type Provider struct { 29 | Primary IProvider 30 | Secondary IProvider 31 | } 32 | -------------------------------------------------------------------------------- /service/component/sms/twilio_provider.go: -------------------------------------------------------------------------------- 1 | package sms 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/kevinburke/twilio-go" 7 | 8 | "github.com/coretrix/hitrix/service/component/clock" 9 | "github.com/coretrix/hitrix/service/component/config" 10 | ) 11 | 12 | const Twilio = "twilio" 13 | 14 | type TwilioProvider struct { 15 | SID string 16 | Token string 17 | FromNumber string 18 | } 19 | 20 | func NewTwilioProvider(configService config.IConfig, _ clock.IClock) (IProvider, error) { 21 | sid, ok := configService.String("sms.twilio.sid") 22 | if !ok { 23 | return nil, errors.New("missing sms.twilio.sid") 24 | } 25 | 26 | token, ok := configService.String("sms.twilio.token") 27 | if !ok { 28 | return nil, errors.New("missing sms.twilio.token") 29 | } 30 | 31 | fromNumberTwilio, ok := configService.String("sms.twilio.from_number") 32 | if !ok { 33 | return nil, errors.New("missing sms.twilio.from_number") 34 | } 35 | 36 | return &TwilioProvider{ 37 | SID: sid, 38 | Token: token, 39 | FromNumber: fromNumberTwilio, 40 | }, nil 41 | } 42 | 43 | func (g *TwilioProvider) GetName() string { 44 | return Twilio 45 | } 46 | 47 | func (g *TwilioProvider) SendSMSMessage(message *Message) (string, error) { 48 | api := twilio.NewClient(g.SID, g.Token, nil) 49 | 50 | _, err := api.Messages.SendMessage(g.FromNumber, message.Number, message.Text, nil) 51 | if err != nil { 52 | return err.Error(), err 53 | } 54 | 55 | return success, nil 56 | } 57 | -------------------------------------------------------------------------------- /service/component/social/facebook.go: -------------------------------------------------------------------------------- 1 | package social 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "io" 7 | "net/http" 8 | "net/url" 9 | ) 10 | 11 | type facebookUserData struct { 12 | ID string `json:"id"` 13 | FirstName string `json:"first_name"` 14 | LastName string `json:"last_name"` 15 | Avatar string `json:"avatar"` 16 | Email string `json:"email"` 17 | } 18 | 19 | type Facebook struct { 20 | } 21 | 22 | func (p *Facebook) GetUserData(_ context.Context, token string, _ bool) (*UserData, error) { 23 | resp, err := http.Get("https://graph.facebook.com/me?access_token=" + 24 | url.QueryEscape(token)) 25 | defer func(body io.ReadCloser) { 26 | _ = body.Close() 27 | }(resp.Body) 28 | 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | // read all response body 34 | body, err := io.ReadAll(resp.Body) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | facebookUser := &facebookUserData{} 40 | 41 | err = json.Unmarshal(body, facebookUser) 42 | if err != nil { 43 | panic(err.Error()) 44 | } 45 | 46 | return &UserData{ 47 | ID: facebookUser.ID, 48 | FirstName: facebookUser.FirstName, 49 | LastName: facebookUser.LastName, 50 | Avatar: facebookUser.Avatar, 51 | Email: facebookUser.Email, 52 | }, nil 53 | } 54 | 55 | func (p *Facebook) SetIsAndroid(_ bool) {} 56 | -------------------------------------------------------------------------------- /service/component/social/google.go: -------------------------------------------------------------------------------- 1 | package social 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "io" 8 | "net/http" 9 | "net/url" 10 | ) 11 | 12 | type googleUserData struct { 13 | ID string `json:"id"` 14 | Email string `json:"email"` 15 | LastName string `json:"family_name"` 16 | FirstName string `json:"given_name"` 17 | Picture string `json:"picture"` 18 | } 19 | 20 | type Google struct { 21 | } 22 | 23 | func (p *Google) GetUserData(_ context.Context, token string, _ bool) (*UserData, error) { 24 | resp, err := http.Get("https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=" + 25 | url.QueryEscape(token)) 26 | defer func(body io.ReadCloser) { 27 | _ = body.Close() 28 | }(resp.Body) 29 | 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | if resp.StatusCode != http.StatusOK { 35 | return nil, errors.New("Status: " + resp.Status) 36 | } 37 | 38 | // read all response body 39 | body, err := io.ReadAll(resp.Body) 40 | if err != nil { 41 | panic(err.Error()) 42 | } 43 | 44 | googleUser := &googleUserData{} 45 | 46 | err = json.Unmarshal(body, googleUser) 47 | if err != nil { 48 | panic(err.Error()) 49 | } 50 | 51 | return &UserData{ 52 | ID: googleUser.ID, 53 | FirstName: googleUser.FirstName, 54 | LastName: googleUser.LastName, 55 | Avatar: googleUser.Picture, 56 | Email: googleUser.Email, 57 | }, nil 58 | } 59 | -------------------------------------------------------------------------------- /service/component/social/mocks/social.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stretchr/testify/mock" 7 | 8 | "github.com/coretrix/hitrix/service/component/social" 9 | ) 10 | 11 | type Social struct { 12 | mock.Mock 13 | } 14 | 15 | func (m *Social) GetUserData(_ context.Context, token string, isAndroid bool) (*social.UserData, error) { 16 | args := m.Called(token, isAndroid) 17 | 18 | return args.Get(0).(*social.UserData), args.Error(1) 19 | } 20 | -------------------------------------------------------------------------------- /service/component/social/social.go: -------------------------------------------------------------------------------- 1 | package social 2 | 3 | import "context" 4 | 5 | type IUserData interface { 6 | GetUserData(ctx context.Context, token string, isAndroid bool) (*UserData, error) 7 | } 8 | 9 | type UserData struct { 10 | ID string 11 | FirstName string 12 | LastName string 13 | Avatar string 14 | Email string 15 | } 16 | -------------------------------------------------------------------------------- /service/component/template/mocks/template.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | ) 6 | 7 | type FakeTemplateService struct { 8 | mock.Mock 9 | } 10 | 11 | func (t *FakeTemplateService) RenderTemplate(html string, data interface{}) (string, error) { 12 | args := t.Called(html, data) 13 | 14 | err := args.Get(1) 15 | if err == nil { 16 | return args.Get(0).(string), nil 17 | } 18 | 19 | return args.Get(0).(string), err.(error) 20 | } 21 | 22 | func (t *FakeTemplateService) RenderMandrillTemplate(template string, data interface{}) (string, error) { 23 | args := t.Called(template, data) 24 | 25 | err := args.Get(1) 26 | if err == nil { 27 | return args.Get(0).(string), nil 28 | } 29 | 30 | return args.Get(0).(string), err.(error) 31 | } 32 | -------------------------------------------------------------------------------- /service/component/template/template.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "bytes" 5 | templatePackage "html/template" 6 | 7 | "github.com/aymerick/raymond" 8 | ) 9 | 10 | type ITemplateInterface interface { 11 | RenderTemplate(html string, data interface{}) (string, error) 12 | RenderMandrillTemplate(template string, data interface{}) (string, error) 13 | } 14 | 15 | type templateService struct { 16 | } 17 | 18 | func (t templateService) RenderTemplate(html string, data interface{}) (string, error) { 19 | var templateBuffer bytes.Buffer 20 | 21 | template, err := templatePackage.New("invoice").Parse(html) 22 | 23 | if err != nil { 24 | return "", err 25 | } 26 | 27 | err = template.Execute(&templateBuffer, data) 28 | if err != nil { 29 | return "", err 30 | } 31 | 32 | return templateBuffer.String(), nil 33 | } 34 | func (t templateService) RenderMandrillTemplate(template string, data interface{}) (string, error) { 35 | html, err := raymond.Render(template, data) 36 | if err != nil { 37 | return "", err 38 | } 39 | 40 | return html, nil 41 | } 42 | 43 | func NewTemplateService() ITemplateInterface { 44 | return &templateService{} 45 | } 46 | -------------------------------------------------------------------------------- /service/component/translation/mocks/translation.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | 6 | "github.com/coretrix/hitrix/datalayer" 7 | "github.com/coretrix/hitrix/pkg/entity" 8 | ) 9 | 10 | type FakeTranslationService struct { 11 | mock.Mock 12 | } 13 | 14 | func (f *FakeTranslationService) GetText(_ *datalayer.ORM, _ entity.TranslationTextLang, key entity.TranslationTextKey) string { 15 | args := f.Called() 16 | if args.Get(0) == nil || args.Get(1) == nil { 17 | return string(key) 18 | } 19 | 20 | return args.String(0) 21 | } 22 | 23 | func (f *FakeTranslationService) GetTextWithVars(_ *datalayer.ORM, 24 | _ entity.TranslationTextLang, 25 | key entity.TranslationTextKey, 26 | _ map[string]interface{}, 27 | ) string { 28 | args := f.Called() 29 | if args.Get(0) == nil || args.Get(1) == nil { 30 | return string(key) 31 | } 32 | 33 | return args.String(0) 34 | } 35 | -------------------------------------------------------------------------------- /service/component/uploader/amazon_store.go: -------------------------------------------------------------------------------- 1 | package uploader 2 | 3 | import ( 4 | tusd "github.com/tus/tusd/pkg/handler" 5 | "github.com/tus/tusd/pkg/s3store" 6 | ) 7 | 8 | type AmazonStore struct { 9 | store s3store.S3Store 10 | } 11 | 12 | func (s *AmazonStore) UseIn(composer *tusd.StoreComposer) { 13 | s.store.UseIn(composer) 14 | } 15 | -------------------------------------------------------------------------------- /service/component/uploader/google_store.go: -------------------------------------------------------------------------------- 1 | package uploader 2 | 3 | import ( 4 | "github.com/tus/tusd/pkg/gcsstore" 5 | tusd "github.com/tus/tusd/pkg/handler" 6 | ) 7 | 8 | type GoogleStore struct { 9 | store gcsstore.GCSStore 10 | } 11 | 12 | func (s *GoogleStore) UseIn(composer *tusd.StoreComposer) { 13 | s.store.UseIn(composer) 14 | } 15 | -------------------------------------------------------------------------------- /service/component/uploader/locker/locker.go: -------------------------------------------------------------------------------- 1 | package locker 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | tusd "github.com/tus/tusd/pkg/handler" 6 | 7 | "github.com/coretrix/hitrix/datalayer" 8 | "github.com/coretrix/hitrix/service" 9 | ) 10 | 11 | type GetLockerFunc func(ctn di.Container) tusd.Locker 12 | 13 | func GetRedisLocker(ctn di.Container) tusd.Locker { 14 | ormService := ctn.Get(service.ORMEngineGlobalService).(*datalayer.ORM) 15 | 16 | return &RedisLocker{ormService: ormService} 17 | } 18 | -------------------------------------------------------------------------------- /service/component/uploader/locker/redis.go: -------------------------------------------------------------------------------- 1 | package locker 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "time" 7 | 8 | "github.com/latolukasz/beeorm/v2" 9 | tusd "github.com/tus/tusd/pkg/handler" 10 | 11 | "github.com/coretrix/hitrix/datalayer" 12 | ) 13 | 14 | type RedisLocker struct { 15 | ormService *datalayer.ORM 16 | } 17 | 18 | func (locker *RedisLocker) NewLock(id string) (tusd.Lock, error) { 19 | return &redisLock{id: id, redis: locker.ormService.GetRedis()}, nil 20 | } 21 | 22 | type redisLock struct { 23 | id string 24 | redis beeorm.RedisCache 25 | redisLock *beeorm.Lock 26 | } 27 | 28 | func (lock *redisLock) Lock() error { 29 | redisLock, obtained := lock.redis.GetLocker().Obtain(context.Background(), "tusd:upload:lock:"+lock.id, time.Hour*24, time.Second*2) 30 | if !obtained { 31 | return errors.New("cannot obtain lock") 32 | } 33 | 34 | lock.redisLock = redisLock 35 | 36 | return nil 37 | } 38 | 39 | func (lock *redisLock) Unlock() error { 40 | lock.redisLock.Release() 41 | 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /service/component/uploader/store.go: -------------------------------------------------------------------------------- 1 | package uploader 2 | 3 | import ( 4 | tusd "github.com/tus/tusd/pkg/handler" 5 | ) 6 | 7 | type Store interface { 8 | UseIn(composer *tusd.StoreComposer) 9 | } 10 | -------------------------------------------------------------------------------- /service/component/uuid/google.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "github.com/google/uuid" 5 | ) 6 | 7 | type Google struct { 8 | } 9 | 10 | func NewGoogleUUID() *Google { 11 | return &Google{} 12 | } 13 | 14 | func (g *Google) Generate() string { 15 | return uuid.New().String() 16 | } 17 | -------------------------------------------------------------------------------- /service/component/uuid/mocks/uuid.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | ) 6 | 7 | type MockUUID struct { 8 | mock.Mock 9 | } 10 | 11 | func (m *MockUUID) Generate() string { 12 | return m.Called().String(0) 13 | } 14 | -------------------------------------------------------------------------------- /service/component/uuid/uuid.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | type IUUID interface { 4 | Generate() string 5 | } 6 | -------------------------------------------------------------------------------- /service/registry/api_logger.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | apilogger "github.com/coretrix/hitrix/service/component/api_logger" 8 | ) 9 | 10 | func ServiceProviderAPILogger(entity apilogger.ILogEntity) *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.APILoggerService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return apilogger.NewMysqlAPILogger(entity), nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/app_definition.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/app" 8 | ) 9 | 10 | func ServiceProviderApp(app *app.App) *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.AppService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return app, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/calendar.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/calendar" 8 | "github.com/coretrix/hitrix/service/component/config" 9 | ) 10 | 11 | func ServiceProviderCalendar(newFunc calendar.NewCalendarFunc) *service.DefinitionGlobal { 12 | return &service.DefinitionGlobal{ 13 | Name: service.CalendarService, 14 | Build: func(ctn di.Container) (interface{}, error) { 15 | return newFunc(ctn.Get(service.ConfigService).(config.IConfig)) 16 | }, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /service/registry/checkout.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/sarulabs/di" 7 | "github.com/xorcare/pointer" 8 | 9 | "github.com/coretrix/hitrix/service" 10 | "github.com/coretrix/hitrix/service/component/app" 11 | "github.com/coretrix/hitrix/service/component/checkout" 12 | "github.com/coretrix/hitrix/service/component/config" 13 | ) 14 | 15 | func ServiceProviderCheckout() *service.DefinitionGlobal { 16 | return &service.DefinitionGlobal{ 17 | Name: service.CheckoutService, 18 | Build: func(ctn di.Container) (interface{}, error) { 19 | configService := ctn.Get(service.ConfigService).(config.IConfig) 20 | 21 | secretKey, ok := configService.String("checkout.secret_key") 22 | if !ok { 23 | return nil, errors.New("missing checkout.secret_key key") 24 | } 25 | 26 | webhookSecrets, ok := configService.StringMap("checkout.webhook_keys") 27 | if !ok { 28 | return nil, errors.New("missing checkout.webhook_keys key") 29 | } 30 | 31 | var publicKey *string 32 | 33 | if publicKeyVal, ok := configService.String("checkout.public_key"); ok && publicKeyVal != "" { 34 | publicKey = pointer.String(publicKeyVal) 35 | } 36 | 37 | appService := ctn.Get(service.AppService).(*app.App) 38 | 39 | return checkout.NewCheckout(secretKey, publicKey, appService.Mode, webhookSecrets), nil 40 | }, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /service/registry/clock.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/clock" 8 | ) 9 | 10 | func ServiceProviderClock() *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.ClockService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return &clock.SysClock{}, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/clockwork.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/coretrix/clockwork" 5 | "github.com/gin-gonic/gin" 6 | 7 | "github.com/coretrix/hitrix/service" 8 | ) 9 | 10 | func ServiceProviderClockWorkForContext() *service.DefinitionRequest { 11 | return &service.DefinitionRequest{ 12 | Name: service.ClockWorkRequestService, 13 | Build: func(c *gin.Context) (interface{}, error) { 14 | return &clockwork.Clockwork{}, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/config.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/config" 8 | ) 9 | 10 | func ServiceProviderConfigDirectory(configDirectory string) *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: "config_directory", 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return configDirectory, nil 15 | }, 16 | } 17 | } 18 | 19 | func ServiceProviderConfig() *service.DefinitionGlobal { 20 | return &service.DefinitionGlobal{ 21 | Name: service.ConfigService, 22 | Build: func(ctn di.Container) (interface{}, error) { 23 | return config.NewConfig(service.DI().App().Name, service.DI().App().Mode, ctn.Get("config_directory").(string)) 24 | }, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /service/registry/crud.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/crud" 8 | "github.com/coretrix/hitrix/service/component/translation" 9 | ) 10 | 11 | func ServiceProviderCrud(exportConfigs []crud.ExportConfig) *service.DefinitionGlobal { 12 | return &service.DefinitionGlobal{ 13 | Name: service.CrudService, 14 | Build: func(ctn di.Container) (interface{}, error) { 15 | translationService, err := ctn.SafeGet(service.TranslationService) 16 | if err == nil { 17 | return &crud.Crud{ExportConfigs: exportConfigs, TranslationService: translationService.(translation.ITranslationService)}, nil 18 | } 19 | 20 | return &crud.Crud{ExportConfigs: exportConfigs}, nil 21 | }, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /service/registry/ddos.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/ddos" 8 | ) 9 | 10 | func ServiceProviderDDOS() *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.DDOSService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return &ddos.DDOS{}, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/elorus.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/sarulabs/di" 7 | 8 | "github.com/coretrix/hitrix/service" 9 | "github.com/coretrix/hitrix/service/component/app" 10 | "github.com/coretrix/hitrix/service/component/config" 11 | "github.com/coretrix/hitrix/service/component/elorus" 12 | ) 13 | 14 | func ServiceProviderElorus() *service.DefinitionGlobal { 15 | return &service.DefinitionGlobal{ 16 | Name: service.ElorusService, 17 | Build: func(ctn di.Container) (interface{}, error) { 18 | configService := ctn.Get(service.ConfigService).(config.IConfig) 19 | 20 | url, ok := configService.String("elorus.url") 21 | if !ok { 22 | return nil, errors.New("missing elorus.token key") 23 | } 24 | 25 | token, ok := configService.String("elorus.token") 26 | if !ok { 27 | return nil, errors.New("missing elorus.token key") 28 | } 29 | 30 | organizationID, ok := configService.String("elorus.organization_id") 31 | if !ok { 32 | return nil, errors.New("missing elorus.organization_id key") 33 | } 34 | 35 | appService := ctn.Get(service.AppService).(*app.App) 36 | 37 | return elorus.NewElorus(url, token, organizationID, appService), nil 38 | }, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /service/registry/error_logger.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/datalayer" 7 | "github.com/coretrix/hitrix/service" 8 | "github.com/coretrix/hitrix/service/component/app" 9 | errorlogger "github.com/coretrix/hitrix/service/component/error_logger" 10 | "github.com/coretrix/hitrix/service/component/sentry" 11 | "github.com/coretrix/hitrix/service/component/slack" 12 | ) 13 | 14 | func ServiceProviderErrorLogger() *service.DefinitionGlobal { 15 | return &service.DefinitionGlobal{ 16 | Name: service.ErrorLoggerService, 17 | Build: func(ctn di.Container) (interface{}, error) { 18 | var sentryService sentry.ISentry 19 | var slackAPIService slack.Slack 20 | 21 | sentryServiceInterface, err := ctn.SafeGet(service.SentryService) 22 | if err == nil { 23 | sentryService = sentryServiceInterface.(sentry.ISentry) 24 | } 25 | 26 | slackAPIServiceInterface, err := ctn.SafeGet(service.SlackService) 27 | if err == nil { 28 | slackAPIService = slackAPIServiceInterface.(slack.Slack) 29 | } 30 | 31 | return errorlogger.NewRedisErrorLogger( 32 | ctn.Get(service.AppService).(*app.App), 33 | ctn.Get(service.ORMEngineGlobalService).(*datalayer.ORM), 34 | slackAPIService, 35 | sentryService, 36 | ), nil 37 | }, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /service/registry/exporter.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/exporter" 8 | ) 9 | 10 | func ServiceProviderExporter() *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.ExporterService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return exporter.NewExportService(exporter.NewXLSXExportService(), exporter.NewCSVExportService()), nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/fcm.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/sarulabs/di" 7 | 8 | "github.com/coretrix/hitrix/service" 9 | "github.com/coretrix/hitrix/service/component/app" 10 | "github.com/coretrix/hitrix/service/component/config" 11 | "github.com/coretrix/hitrix/service/component/fcm" 12 | ) 13 | 14 | const ( 15 | fcmConfigEnvName = "FIREBASE_CONFIG" 16 | googleAuthConfigEnvName = "GOOGLE_APPLICATION_CREDENTIALS" 17 | ) 18 | 19 | func ServiceProviderFCM() *service.DefinitionGlobal { 20 | return &service.DefinitionGlobal{ 21 | Name: service.FCMService, 22 | Build: func(ctn di.Container) (interface{}, error) { 23 | val, present := os.LookupEnv(fcmConfigEnvName) 24 | 25 | configService := ctn.Get(service.ConfigService).(config.IConfig) 26 | 27 | if present { 28 | if _, err := os.Stat(val); err != nil { 29 | if os.IsNotExist(err) { 30 | // specified config file doesn't exists 31 | credentialsFile := configService.GetFolderPath() + "/.fcm.json" 32 | err := os.Setenv(fcmConfigEnvName, credentialsFile) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | err = os.Setenv(googleAuthConfigEnvName, credentialsFile) 38 | if err != nil { 39 | return nil, err 40 | } 41 | } 42 | } 43 | } 44 | 45 | return fcm.NewFCM(ctn.Get(service.AppService).(*app.App).GlobalContext) 46 | }, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /service/registry/file_extractor.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | fileextractor "github.com/coretrix/hitrix/service/component/file_extractor" 8 | ) 9 | 10 | func ServiceProviderExtractor() *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.ExtractorService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return fileextractor.NewFileExtractor(), nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/generator.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/generator" 8 | ) 9 | 10 | func ServiceProviderGenerator() *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.GeneratorService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return &generator.SimpleGenerator{}, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/google_analytics.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/config" 8 | errorlogger "github.com/coretrix/hitrix/service/component/error_logger" 9 | googleanalytics "github.com/coretrix/hitrix/service/component/google_analytics" 10 | ) 11 | 12 | func ServiceProviderGoogleAnalytics(newFunctions googleanalytics.NewProviderFunc) *service.DefinitionGlobal { 13 | return &service.DefinitionGlobal{ 14 | Name: service.GoogleAnalyticsService, 15 | Build: func(ctn di.Container) (interface{}, error) { 16 | return googleanalytics.NewAPIManager( 17 | ctn.Get("config_directory").(string), 18 | ctn.Get(service.ConfigService).(config.IConfig), 19 | ctn.Get(service.ErrorLoggerService).(errorlogger.ErrorLogger), 20 | newFunctions) 21 | }, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /service/registry/goroutine.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | errorlogger "github.com/coretrix/hitrix/service/component/error_logger" 8 | "github.com/coretrix/hitrix/service/component/goroutine" 9 | ) 10 | 11 | func ServiceProviderGoroutine() *service.DefinitionGlobal { 12 | return &service.DefinitionGlobal{ 13 | Name: service.GoroutineService, 14 | Build: func(ctn di.Container) (interface{}, error) { 15 | return goroutine.NewGoroutineManager(ctn.Get(service.ErrorLoggerService).(errorlogger.ErrorLogger)), nil 16 | }, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /service/registry/gql.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/gql" 8 | "github.com/coretrix/hitrix/service/component/localize" 9 | ) 10 | 11 | func ServiceProviderGql() *service.DefinitionGlobal { 12 | return &service.DefinitionGlobal{ 13 | Name: service.GQLService, 14 | Build: func(ctn di.Container) (interface{}, error) { 15 | localizeService, err := ctn.SafeGet(service.LocalizeService) 16 | if err == nil { 17 | return gql.NewGqlService(localizeService.(localize.ILocalizer)), nil 18 | } 19 | 20 | return gql.NewGqlService(nil), nil 21 | }, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /service/registry/html2pdf.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/sarulabs/di" 7 | 8 | "github.com/coretrix/hitrix/service" 9 | "github.com/coretrix/hitrix/service/component/config" 10 | "github.com/coretrix/hitrix/service/component/html2pdf" 11 | ) 12 | 13 | func ServiceProviderHTML2PDF() *service.DefinitionGlobal { 14 | return &service.DefinitionGlobal{ 15 | Name: service.HTML2PDFService, 16 | Build: func(ctn di.Container) (interface{}, error) { 17 | configService := ctn.Get(service.ConfigService).(config.IConfig) 18 | chromeWebSocketURL, ok := configService.String("chrome_headless.web_socket_url") 19 | if !ok { 20 | return nil, errors.New("missing chrome_headless.web_socket_url") 21 | } 22 | 23 | return html2pdf.NewHTML2PDFService(chromeWebSocketURL), nil 24 | }, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /service/registry/instagram.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/config" 8 | "github.com/coretrix/hitrix/service/component/instagram" 9 | ) 10 | 11 | func ServiceProviderInstagram(newFunctions instagram.NewProviderFunc) *service.DefinitionGlobal { 12 | return &service.DefinitionGlobal{ 13 | Name: service.InstagramService, 14 | Build: func(ctn di.Container) (interface{}, error) { 15 | return instagram.NewAPIManager( 16 | ctn.Get(service.ConfigService).(config.IConfig), 17 | newFunctions) 18 | }, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /service/registry/jwt.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/jwt" 8 | ) 9 | 10 | func ServiceProviderJWT() *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.JWTService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return &jwt.JWT{}, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/kubernetes.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path" 7 | 8 | "github.com/sarulabs/di" 9 | 10 | "github.com/coretrix/hitrix/service" 11 | "github.com/coretrix/hitrix/service/component/config" 12 | "github.com/coretrix/hitrix/service/component/kubernetes" 13 | ) 14 | 15 | func ServiceProviderKubernetes() *service.DefinitionGlobal { 16 | return &service.DefinitionGlobal{ 17 | Name: service.KubernetesService, 18 | Build: func(ctn di.Container) (interface{}, error) { 19 | configService := ctn.Get(service.ConfigService).(config.IConfig) 20 | 21 | configFilePath := "" 22 | configFile, ok := configService.String("kubernetes.config_file") 23 | if ok { 24 | var configFolder string 25 | 26 | if path.IsAbs(configFile) { 27 | configFilePath = configFile 28 | } else { 29 | appFolder, hasConfigFolder := os.LookupEnv("APP_FOLDER") 30 | if !hasConfigFolder { 31 | configFolder = ctn.Get("config_directory").(string) 32 | } else { 33 | configFolder = appFolder + "/config" 34 | } 35 | 36 | configFilePath = path.Join(configFolder, configFile) 37 | } 38 | } 39 | 40 | environment, ok := configService.String("kubernetes.environment") 41 | if !ok { 42 | return nil, errors.New("missing kubernetes.environment") 43 | } 44 | 45 | return kubernetes.NewKubernetes(configFilePath, environment), nil 46 | }, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /service/registry/license_plate_recognizer.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/sarulabs/di" 7 | 8 | "github.com/coretrix/hitrix/service" 9 | "github.com/coretrix/hitrix/service/component/config" 10 | licenseplaterecognizer "github.com/coretrix/hitrix/service/component/license_plate_recognizer" 11 | ) 12 | 13 | func ServiceProviderLicensePlateRecognizer() *service.DefinitionGlobal { 14 | return &service.DefinitionGlobal{ 15 | Name: service.LicensePlateRecognizerService, 16 | Build: func(ctn di.Container) (interface{}, error) { 17 | configService := ctn.Get(service.ConfigService).(config.IConfig) 18 | apiKey, ok := configService.String("platerecognizer.api_key") 19 | if !ok { 20 | return nil, errors.New("missing platerecognizer.api_key") 21 | } 22 | 23 | return licenseplaterecognizer.NewPlateRecognizer(apiKey), nil 24 | }, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /service/registry/mail.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | "github.com/sarulabs/di" 8 | 9 | "github.com/coretrix/hitrix/service" 10 | "github.com/coretrix/hitrix/service/component/clock" 11 | "github.com/coretrix/hitrix/service/component/config" 12 | errorlogger "github.com/coretrix/hitrix/service/component/error_logger" 13 | "github.com/coretrix/hitrix/service/component/mail" 14 | ) 15 | 16 | // ServiceProviderMail Be sure that you registered entity MailTrackerEntity 17 | func ServiceProviderMail(newFunc mail.NewSenderFunc) *service.DefinitionGlobal { 18 | return &service.DefinitionGlobal{ 19 | Name: service.MailService, 20 | Build: func(ctn di.Container) (interface{}, error) { 21 | ormConfig := ctn.Get(service.ORMConfigService).(beeorm.ValidatedRegistry) 22 | entities := ormConfig.GetEntities() 23 | if _, ok := entities["entity.MailTrackerEntity"]; !ok { 24 | return nil, errors.New("you should register MailTrackerEntity") 25 | } 26 | 27 | configService := ctn.Get(service.ConfigService).(config.IConfig) 28 | clockService := ctn.Get(service.ClockService).(clock.IClock) 29 | 30 | provider, err := newFunc(ctn.Get(service.ConfigService).(config.IConfig)) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | return &mail.Sender{ 36 | ConfigService: configService, 37 | ClockService: clockService, 38 | Provider: provider, 39 | ErrorLoggerService: ctn.Get(service.ErrorLoggerService).(errorlogger.ErrorLogger), 40 | }, nil 41 | }, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /service/registry/mocks/calendar.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/calendar" 8 | ) 9 | 10 | func ServiceProviderMockCalendar(mock calendar.ICalendar) *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.CalendarService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return mock, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/mocks/checkout.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockCheckout(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.CheckoutService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/clock.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/clock" 8 | ) 9 | 10 | func ServiceProviderMockClock(mock clock.IClock) *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.ClockService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return mock, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/mocks/dynamic_link.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockDynamicLink(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.DynamicLinkService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/elorus.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/elorus" 8 | ) 9 | 10 | func ServiceProviderMockElorus(mock elorus.IProvider) *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.ElorusService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return mock, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/mocks/exporter.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockExporter(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.ExporterService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/fcm.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/fcm" 8 | ) 9 | 10 | func ServiceProviderMockFCM(mock fcm.FCM) *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.FCMService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return mock, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/mocks/feature_flag.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceFeatureFlag(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.FeatureFlagService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/generator.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockGenerator(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.GeneratorService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/geocoding.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockGeocoding(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.GeocodingService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/google_analytics.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockGoogleAnalytics(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.GoogleAnalyticsService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/instagram.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockInstagram(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.InstagramService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/kubernetes.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockKubernetes(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.KubernetesService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/license_plate_recognizer.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockLicensePlateRecognizer(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.LicensePlateRecognizerService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/mail.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/mail" 8 | ) 9 | 10 | func ServiceProviderMockMail(mock mail.ISender) *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.MailService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return mock, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/mocks/oss.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/oss" 8 | ) 9 | 10 | func ServiceProviderMockOSS(mock oss.IProvider) *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.OSService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return mock, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/mocks/otp.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockOTP(fake interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.OTPService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return fake, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/pdf.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func FakeServiceTemplate(fake interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.HTML2PDFService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return fake, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/s3.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockAmazonS3(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.AmazonS3Service, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/sentry.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/sentry" 8 | ) 9 | 10 | func ServiceProviderMockSentry(mock sentry.ISentry) *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.SentryService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return mock, nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/mocks/setting.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceSetting(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.SettingService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/sms.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockSMS(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.SMSService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/stripe.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockStripe(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.StripeService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/template.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func FakeServiceSTemplate(fake interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.TemplateService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return fake, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/mocks/uuid.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | ) 8 | 9 | func ServiceProviderMockUUID(mock interface{}) *service.DefinitionGlobal { 10 | return &service.DefinitionGlobal{ 11 | Name: service.UUIDService, 12 | Build: func(ctn di.Container) (interface{}, error) { 13 | return mock, nil 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/registry/oss.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | "github.com/sarulabs/di" 8 | 9 | "github.com/coretrix/hitrix/service" 10 | "github.com/coretrix/hitrix/service/component/clock" 11 | "github.com/coretrix/hitrix/service/component/config" 12 | "github.com/coretrix/hitrix/service/component/oss" 13 | ) 14 | 15 | func ServiceProviderOSS(newFunc oss.NewProviderFunc, namespaces oss.Namespaces) *service.DefinitionGlobal { 16 | return &service.DefinitionGlobal{ 17 | Name: service.OSService, 18 | Build: func(ctn di.Container) (interface{}, error) { 19 | ormConfig := ctn.Get(service.ORMConfigService).(beeorm.ValidatedRegistry) 20 | 21 | entities := ormConfig.GetEntities() 22 | 23 | if _, ok := entities["entity.OSSBucketCounterEntity"]; !ok { 24 | return nil, errors.New("you should register OSSBucketCounterEntity") 25 | } 26 | 27 | return newFunc( 28 | ctn.Get(service.ConfigService).(config.IConfig), 29 | ctn.Get(service.ClockService).(clock.IClock), 30 | namespaces) 31 | }, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /service/registry/password.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/config" 8 | "github.com/coretrix/hitrix/service/component/password" 9 | ) 10 | 11 | func ServiceProviderPassword(newFunc password.NewPasswordManagerFunc) *service.DefinitionGlobal { 12 | return &service.DefinitionGlobal{ 13 | Name: service.PasswordService, 14 | Build: func(ctn di.Container) (interface{}, error) { 15 | return newFunc(ctn.Get(service.ConfigService).(config.IConfig)), nil 16 | }, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /service/registry/request_logger.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/clock" 8 | requestlogger "github.com/coretrix/hitrix/service/component/request_logger" 9 | ) 10 | 11 | func ServiceProviderRequestLogger() *service.DefinitionGlobal { 12 | return &service.DefinitionGlobal{ 13 | Name: service.RequestLoggerService, 14 | Build: func(ctn di.Container) (interface{}, error) { 15 | ormConfig := service.DI().OrmConfig() 16 | 17 | entities := ormConfig.GetEntities() 18 | if _, ok := entities["entity.RequestLoggerEntity"]; !ok { 19 | panic("you should register RequestLoggerEntity") 20 | } 21 | 22 | return requestlogger.NewDBLogger(ctn.Get(service.ClockService).(clock.IClock)), nil 23 | }, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /service/registry/sentry.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/app" 8 | "github.com/coretrix/hitrix/service/component/config" 9 | "github.com/coretrix/hitrix/service/component/sentry" 10 | ) 11 | 12 | func ServiceProviderSentry(tracesSampleRate *float64) *service.DefinitionGlobal { 13 | return &service.DefinitionGlobal{ 14 | Name: service.SentryService, 15 | Build: func(ctn di.Container) (interface{}, error) { 16 | configService := ctn.Get(service.ConfigService).(config.IConfig) 17 | if configService == nil { 18 | panic("`config is nil") 19 | } 20 | 21 | backendVersionFinal := "" 22 | backendVersion, ok := configService.String("backend_version") 23 | if ok { 24 | backendVersionFinal = backendVersion 25 | } 26 | 27 | dsn, ok := configService.String("sentry.dsn") 28 | if !ok { 29 | panic("required config value sentry.dsn missing") 30 | } 31 | 32 | appService := ctn.Get(service.AppService).(*app.App) 33 | if appService == nil { 34 | panic("`app is nil") 35 | } 36 | 37 | return sentry.Init(dsn, appService.Mode, backendVersionFinal, tracesSampleRate), nil 38 | }, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /service/registry/setting.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/setting" 8 | ) 9 | 10 | func ServiceProviderSetting() *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.SettingService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return setting.NewSettingService(), nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/slack.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/sarulabs/di" 7 | 8 | "github.com/coretrix/hitrix/service" 9 | "github.com/coretrix/hitrix/service/component/config" 10 | "github.com/coretrix/hitrix/service/component/slack" 11 | ) 12 | 13 | func ServiceProviderSlack() *service.DefinitionGlobal { 14 | return &service.DefinitionGlobal{ 15 | Name: service.SlackService, 16 | Build: func(ctn di.Container) (interface{}, error) { 17 | configService := ctn.Get(service.ConfigService).(config.IConfig) 18 | 19 | botTokensConfig, ok := configService.Get("slack.bot_tokens") 20 | if !ok { 21 | return nil, errors.New("missing slack.bot_tokens") 22 | } 23 | 24 | botTokens := make(map[string]string) 25 | 26 | for name, token := range botTokensConfig.(map[interface{}]interface{}) { 27 | botTokens[name.(string)] = token.(string) 28 | } 29 | 30 | errorChannel, _ := configService.String("slack.error_channel") 31 | devPanelURL, _ := configService.String("slack.dev_panel_url") 32 | 33 | return slack.NewSlackGo(botTokens, errorChannel, devPanelURL), nil 34 | }, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /service/registry/socket.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/goroutine" 8 | "github.com/coretrix/hitrix/service/component/socket" 9 | ) 10 | 11 | func ServiceProviderSocketRegistry(eventHandlersMap socket.NamespaceEventHandlerMap) *service.DefinitionGlobal { 12 | return &service.DefinitionGlobal{ 13 | Name: service.SocketRegistryService, 14 | Build: func(ctn di.Container) (interface{}, error) { 15 | return socket.NewSocketRegistry( 16 | eventHandlersMap, 17 | ctn.Get(service.GoroutineService).(goroutine.IGoroutine)), 18 | nil 19 | }, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /service/registry/stripe.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/sarulabs/di" 7 | 8 | "github.com/coretrix/hitrix/service" 9 | "github.com/coretrix/hitrix/service/component/app" 10 | "github.com/coretrix/hitrix/service/component/config" 11 | "github.com/coretrix/hitrix/service/component/stripe" 12 | ) 13 | 14 | func ServiceProviderStripe() *service.DefinitionGlobal { 15 | return &service.DefinitionGlobal{ 16 | Name: service.StripeService, 17 | Build: func(ctn di.Container) (interface{}, error) { 18 | configService := ctn.Get(service.ConfigService).(config.IConfig) 19 | 20 | key, ok := configService.String("stripe.key") 21 | if !ok { 22 | return nil, errors.New("missing stripe key") 23 | } 24 | 25 | secrets, ok := configService.StringMap("stripe.webhook_secrets") 26 | if !ok { 27 | return nil, errors.New("missing stripe secrets") 28 | } 29 | 30 | appService := ctn.Get(service.AppService).(*app.App) 31 | 32 | return stripe.NewStripe(key, secrets, appService), nil 33 | }, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /service/registry/template.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/template" 8 | ) 9 | 10 | func ServiceProviderTemplate() *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.TemplateService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return template.NewTemplateService(), nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/registry/translation.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/latolukasz/beeorm/v2" 7 | "github.com/sarulabs/di" 8 | 9 | "github.com/coretrix/hitrix/service" 10 | errorlogger "github.com/coretrix/hitrix/service/component/error_logger" 11 | "github.com/coretrix/hitrix/service/component/translation" 12 | ) 13 | 14 | func ServiceProviderTranslation() *service.DefinitionGlobal { 15 | return &service.DefinitionGlobal{ 16 | Name: service.TranslationService, 17 | Build: func(ctn di.Container) (interface{}, error) { 18 | ormConfig := ctn.Get(service.ORMConfigService).(beeorm.ValidatedRegistry) 19 | entities := ormConfig.GetEntities() 20 | 21 | if _, ok := entities["entity.TranslationTextEntity"]; !ok { 22 | return nil, errors.New("you should register TranslationTextEntity") 23 | } 24 | 25 | return translation.NewTranslationService(ctn.Get(service.ErrorLoggerService).(errorlogger.ErrorLogger)), nil 26 | }, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /service/registry/uploader.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | tusd "github.com/tus/tusd/pkg/handler" 6 | 7 | "github.com/coretrix/hitrix/service" 8 | "github.com/coretrix/hitrix/service/component/oss" 9 | "github.com/coretrix/hitrix/service/component/uploader" 10 | "github.com/coretrix/hitrix/service/component/uploader/locker" 11 | ) 12 | 13 | func ServiceProviderUploader(c tusd.Config, getLockerFunc locker.GetLockerFunc) *service.DefinitionGlobal { 14 | return &service.DefinitionGlobal{ 15 | Name: service.UploaderService, 16 | Build: func(ctn di.Container) (interface{}, error) { 17 | osService := ctn.Get(service.OSService).(oss.IProvider) 18 | 19 | uploaderBucketName := osService.GetBucketConfig(oss.BucketPublic).Name 20 | 21 | composer := tusd.NewStoreComposer() 22 | 23 | store := uploader.GetStore(osService.GetClient(), uploaderBucketName) 24 | store.UseIn(composer) 25 | 26 | if getLockerFunc != nil { 27 | composer.UseLocker(getLockerFunc(ctn)) 28 | } 29 | 30 | c.StoreComposer = composer 31 | 32 | return uploader.NewTUSDUploader(c, uploaderBucketName), nil 33 | }, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /service/registry/uuid.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/sarulabs/di" 5 | 6 | "github.com/coretrix/hitrix/service" 7 | "github.com/coretrix/hitrix/service/component/uuid" 8 | ) 9 | 10 | func ServiceProviderUUID() *service.DefinitionGlobal { 11 | return &service.DefinitionGlobal{ 12 | Name: service.UUIDService, 13 | Build: func(ctn di.Container) (interface{}, error) { 14 | return uuid.NewGoogleUUID(), nil 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/api_logger_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/coretrix/hitrix/example/entity" 9 | "github.com/coretrix/hitrix/service" 10 | "github.com/coretrix/hitrix/service/registry" 11 | ) 12 | 13 | func TestApiLogger(t *testing.T) { 14 | createContextMyApp(t, "server", nil, 15 | []*service.DefinitionGlobal{ 16 | registry.ServiceProviderAPILogger(&entity.APILogEntity{}), 17 | }, 18 | nil, 19 | ) 20 | 21 | apiLoggerService := service.DI().APILogger() 22 | 23 | ormService := service.DI().OrmEngine() 24 | apiLoggerService.LogStart(ormService, entity.APILogTypeApple, nil) 25 | apiLoggerService.LogSuccess(ormService, nil) 26 | 27 | apiLoggerService.LogStart(ormService, entity.APILogTypeApple, nil) 28 | apiLoggerService.LogError(ormService, "Error appear", nil) 29 | 30 | var apiLogEntities []*entity.APILogEntity 31 | ormService.LoadByIDs([]uint64{1, 2}, &apiLogEntities) 32 | assert.Len(t, apiLogEntities, 2) 33 | assert.Equal(t, apiLogEntities[0].Status, entity.APILogStatusCompleted) 34 | assert.Equal(t, apiLogEntities[1].Status, entity.APILogStatusFailed) 35 | } 36 | -------------------------------------------------------------------------------- /test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/sarulabs/di" 8 | 9 | "github.com/coretrix/hitrix" 10 | "github.com/coretrix/hitrix/service" 11 | "github.com/coretrix/hitrix/service/component/app" 12 | "github.com/coretrix/hitrix/service/registry" 13 | ) 14 | 15 | type testScript struct { 16 | description string 17 | unique bool 18 | } 19 | 20 | func (script *testScript) Run(_ context.Context, _ app.IExit) { 21 | } 22 | 23 | func (script *testScript) Unique() bool { 24 | return script.unique 25 | } 26 | 27 | func (script *testScript) Description() string { 28 | return script.description 29 | } 30 | 31 | func (script *testScript) Active() bool { 32 | return true 33 | } 34 | 35 | func (script *testScript) Interval() time.Duration { 36 | return 3 * time.Second 37 | } 38 | 39 | func main() { 40 | r := hitrix.New("test_script", "secret") 41 | r.RegisterDIGlobalService(&service.DefinitionGlobal{ 42 | Name: "aa", 43 | 44 | Script: true, 45 | Build: func(ctn di.Container) (interface{}, error) { 46 | return &testScript{"takie tam", false}, nil 47 | }, 48 | }) 49 | r.RegisterDIGlobalService(&service.DefinitionGlobal{ 50 | Name: "bb", 51 | 52 | Script: true, 53 | Build: func(ctn di.Container) (interface{}, error) { 54 | return &testScript{"takie tam dwa", true}, nil 55 | }, 56 | }) 57 | r.RegisterDIGlobalService(registry.ServiceProviderConfigDirectory("../config")) 58 | r.RegisterDIGlobalService().Build() 59 | } 60 | -------------------------------------------------------------------------------- /test/redis_search_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | redisearch "github.com/coretrix/beeorm-redisearch-plugin" 7 | "github.com/stretchr/testify/assert" 8 | 9 | "github.com/coretrix/hitrix/example/entity" 10 | "github.com/coretrix/hitrix/service" 11 | ) 12 | 13 | func TestRedisSearch(t *testing.T) { 14 | createContextMyApp(t, "server", nil, nil, nil) 15 | 16 | ormService := service.DI().OrmEngine() 17 | 18 | query := &redisearch.RedisSearchQuery{} 19 | query.FilterString("Email", "test@coretrix.com") 20 | 21 | newDevPanelUserEntity := &entity.DevPanelUserEntity{ 22 | Email: "test@coretrix.com", 23 | } 24 | ormService.Flush(newDevPanelUserEntity) 25 | 26 | devPanelUserEntity := &entity.DevPanelUserEntity{} 27 | found := ormService.RedisSearchOne(devPanelUserEntity, query) 28 | 29 | assert.True(t, found) 30 | } 31 | -------------------------------------------------------------------------------- /test/script_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | "github.com/coretrix/hitrix" 10 | "github.com/coretrix/hitrix/service" 11 | "github.com/coretrix/hitrix/service/component/app" 12 | ) 13 | 14 | type testScript2 struct { 15 | RunCounter int 16 | } 17 | 18 | func (script *testScript2) Run(_ context.Context, _ app.IExit) { 19 | script.RunCounter++ 20 | } 21 | 22 | func (script *testScript2) Unique() bool { 23 | return false 24 | } 25 | 26 | func (script *testScript2) Code() string { 27 | return "test script" 28 | } 29 | 30 | func (script *testScript2) Description() string { 31 | return "test description" 32 | } 33 | 34 | func TestRunScript(t *testing.T) { 35 | env := createContextMyApp(t, "server", nil, nil, nil) 36 | 37 | testScript2 := &testScript2{} 38 | b := &hitrix.BackgroundProcessor{Server: env.Hitrix} 39 | b.RunScript(testScript2) 40 | assert.Equal(t, 1, testScript2.RunCounter) 41 | assert.Equal(t, "server", service.DI().App().Name) 42 | } 43 | -------------------------------------------------------------------------------- /test/validator_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | "github.com/coretrix/hitrix/pkg/binding" 10 | "github.com/coretrix/hitrix/pkg/helper" 11 | ) 12 | 13 | func TestBasicValidation(t *testing.T) { 14 | err := binding.NewValidator().Validate("no-email-string", "email") 15 | assert.NotNil(t, err) 16 | 17 | err = binding.NewValidator().Validate("awesome-dude@awesome-com.com", "email") 18 | assert.Nil(t, err) 19 | } 20 | 21 | func TestCountryCodeValidation(t *testing.T) { 22 | err := binding.NewValidator().Validate("ABCDEFG", "country_code_custom") 23 | assert.NotNil(t, err) 24 | 25 | err = binding.NewValidator().Validate("SE", "country_code_custom") 26 | assert.Nil(t, err) 27 | } 28 | 29 | func TestTimestampGteTodayValidation(t *testing.T) { 30 | now := time.Now() 31 | valueBeforeNow := time.Now().AddDate(0, 0, -1) 32 | err := binding.NewValidator().Validate(helper.GetTimestamp(&valueBeforeNow), "timestamp_gte_today") 33 | assert.NotNil(t, err) 34 | 35 | err = binding.NewValidator().Validate(helper.GetTimestamp(&now), "timestamp_gte_today") 36 | assert.Nil(t, err) 37 | } 38 | -------------------------------------------------------------------------------- /validator.go: -------------------------------------------------------------------------------- 1 | package hitrix 2 | 3 | import ( 4 | "context" 5 | "reflect" 6 | 7 | "github.com/99designs/gqlgen/graphql" 8 | "github.com/vektah/gqlparser/v2/gqlerror" 9 | 10 | "github.com/coretrix/hitrix/pkg/binding" 11 | ) 12 | 13 | func ValidateDirective() func(ctx context.Context, obj interface{}, next graphql.Resolver, rules string) (interface{}, error) { 14 | return func(ctx context.Context, obj interface{}, next graphql.Resolver, rules string) (interface{}, error) { 15 | originalValue, err := next(ctx) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | switch v := originalValue.(type) { 21 | case string: 22 | if v == "" { 23 | return v, nil 24 | } 25 | default: 26 | if v == nil || reflect.ValueOf(v).IsNil() { 27 | return v, nil 28 | } 29 | } 30 | 31 | errs := binding.NewValidator().Validate(originalValue, rules) 32 | if len(errs) > 0 { 33 | if len(errs) > 1 { 34 | for i := 1; i < len(errs); i++ { 35 | graphql.AddError(ctx, &gqlerror.Error{ 36 | Path: graphql.GetPath(ctx), 37 | Message: "Field" + errs[i].Error(), 38 | }) 39 | } 40 | } 41 | 42 | return nil, &gqlerror.Error{ 43 | Path: graphql.GetPath(ctx), 44 | Message: "Field" + errs[0].Error(), 45 | } 46 | } 47 | 48 | return originalValue, nil 49 | } 50 | } 51 | 52 | func Validate(ctx context.Context, callback func() bool) bool { 53 | if graphql.GetErrors(ctx) != nil { 54 | return false 55 | } 56 | 57 | if callback != nil { 58 | return callback() 59 | } 60 | 61 | return true 62 | } 63 | --------------------------------------------------------------------------------