{"id":22260802,"url":"https://github.com/hrvadl/converter","last_synced_at":"2026-05-04T02:38:12.316Z","repository":{"id":240039896,"uuid":"801503736","full_name":"hrvadl/converter","owner":"hrvadl","description":"Microservice for subscribing emails \u0026 notifying them once a day when exchange rate changed","archived":false,"fork":false,"pushed_at":"2024-06-07T06:21:45.000Z","size":228,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-06-07T15:19:48.844Z","etag":null,"topics":["exchange-rates","go","golang","grpc","microservices","server","sqlx"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hrvadl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-16T11:05:06.000Z","updated_at":"2024-06-19T04:17:36.506Z","dependencies_parsed_at":"2024-06-07T07:34:12.904Z","dependency_job_id":"e2ec8854-86d6-41c8-b822-c55477274465","html_url":"https://github.com/hrvadl/converter","commit_stats":null,"previous_names":["hrvadl/btcratenotifier","hrvadl/converter"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrvadl%2Fconverter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrvadl%2Fconverter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrvadl%2Fconverter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrvadl%2Fconverter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hrvadl","download_url":"https://codeload.github.com/hrvadl/converter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245469571,"owners_count":20620611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["exchange-rates","go","golang","grpc","microservices","server","sqlx"],"created_at":"2024-12-03T09:10:03.067Z","updated_at":"2026-05-04T02:38:07.290Z","avatar_url":"https://github.com/hrvadl.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# USD -\u003e UAH rate notifier microservice 💸\n\n## Description 💬\n\nThe app contains 4 microservices:\n\n- Subscriber (sub) for (un)subscribing users.\n- Gateway (gw) for mapping HTTP -\u003e GRPC requests and entry point purposes. It's autogenerated with [go-grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway)\n- Mailer - service for sending daily emails. Has cron job to trigger email once a day at 12:00 UTC (15:00 by Kyiv time)\n- RateWatcher (rw) - service for getting the latest currency exchange rates\n- Shared package (pkg) - pkg containing code, which can be used accross modules (logger setup etc.)\n\nAs per the task, I need to send a link to only one repository, it was decided to use go workspaces to fit all microservices to one repo. Typically, it should not be the case and it's antipattern. Basically, you can treat each top-level directory as a separate and independent repository/package/module. The `protos` top-level directory is also a go module, containing grpc-generated code.\n\n## Tech stack ⚒️\n\n- [GO](https://go.dev/) as a main programming language\n- [`net/http`](https://pkg.go.dev/net/http) for HTTP server\n- [GRPC](https://grpc.io/) for inter-service communication\n- [`log/slog`](https://go.dev/blog/slog) as a structured logger\n- [swaggo](https://github.com/swaggo/swag) for generating swagger documentaion\n- [MySQL](https://www.mysql.com/) as a database\n- [Golang migrate](https://github.com/golang-migrate/migrate) for DB migrations\n- [Gomock](https://github.com/uber-go/mock) for mock generation\n\n## Demo 🎤\n\nhttps://drive.google.com/file/d/1jrEaqvzlhKB4HB98VEykbiR00G2OEwJS/view?usp=sharing\n\n## How to run? 🏃\n\n1. Copy .env.example contents to .env\n2. Get a token for exchange rate API (https://app.exchangerate-api.com/)\n3. Populate the `EXCHANGE_API_KEY` variable value with the token you've got\n4. Get a token for resend API (https://resend.com/)\n5. Verify your domain for sending\n6. Populate the `MAILER_API_KEY` variable value with the token you've got\n7. Populate the `MAILER_FROM_ADDR` variable with the email you've verified\n8. Populate other variables, such as `MAILER_SMTP_FROM` etc.\n9. From the root of the repo run `docker compose up -d`\n\n## Local development 🧑🏻‍💻\n\nThe repository contains root [taskfile](https://taskfile.dev/) which imports other task files specific to each service. To see all available commands just type:\n\n```sh\ntask\n```\n\nYou should get the following output, where each line is the name of the task:\n\n```sh\n* default:                      Show available tasks\n* lint:\n* test:\n* gw:generate:                  Generate (used for mock generation)\n* gw:install:                   Install all tools\n* gw:install:gofumpt:           Install gofumpt\n* gw:install:lint:              Install golangci-lint\n* gw:install:mock:              Install mockgen\n* gw:lint:                      Run golangci-lint\n* gw:run:\n* gw:test:                      Run tests\n* gw:test:cover:                Run tests \u0026 show coverage\n* gw:test:race:                 Run tests with a race flag\n* mailer:generate:              Generate (used for mock generation)\n* mailer:install:               Install all tools\n* mailer:install:gofumpt:       Install gofumpt\n* mailer:install:lint:          Install golangci-lint\n* mailer:install:mock:          Install mockgen\n* mailer:lint:                  Run golangci-lint\n* mailer:run:\n* mailer:test:                  Run tests\n* mailer:test:cover:            Run tests \u0026 show coverage\n* mailer:test:race:             Run tests with a race flag\n* protos:generate:\n* protos:generate:mailer:\n* protos:generate:rw:\n* protos:generate:sub:\n* rw:generate:                  Generate (used for mock generation)\n* rw:install:                   Install all tools\n* rw:install:gofumpt:           Install gofumpt\n* rw:install:lint:              Install golangci-lint\n* rw:install:mock:              Install mockgen\n* rw:lint:                      Run golangci-lint\n* rw:run:\n* rw:test:                      Run tests\n* rw:test:cover:                Run tests \u0026 show coverage\n* rw:test:race:                 Run tests with a race flag\n* sub:generate:                 Generate (used for mock generation)\n* sub:install:                  Install all tools\n* sub:install:gofumpt:          Install gofumpt\n* sub:install:lint:             Install golangci-lint\n* sub:install:mock:             Install mockgen\n* sub:lint:                     Run golangci-lint\n* sub:run:\n* sub:test:                     Run tests\n* sub:test:cover:               Run tests \u0026 show coverage\n* sub:test:race:                Run tests with a race flag\n```\n\nThey're quite handy to run tests/linters/formatters or to generate grpc-related code and they will install all the deps in case you don't have them.\n\nBefore committing anything, you need to also install [left-hook](https://github.com/evilmartians/lefthook). It will run golangci lint before committing to prevent common dummy issues related to formatting/go code. Typically, it should be covered by the CI job, but I need to reduce possible extra runs, considering GH actions has limit on daily runs.\n\n## Compose file 🐋\n\nCompose file has definition of 4 microservices + 1 db service (MySQl) + migrator service (used for running DB migrations on startup). Typically, services won't start till DB is up and migrations has succeeded. So, keep in head, that startup could take some time (1-2 minutes). Data is saved in volume and pods communicate using shared private network. The only pod, which port is mapped to the host port is gateway service.\n\n## Documentation 📄\n\nServices are perfectly documented with Godoc comments. You can host godoc server running following command from the root of the repo:\n\n```sh\ntask godoc\n```\n\nThen you can visit http://localhost:6060/pkg/github.com/hrvadl/converter/?m=all and see documentation for all my packages\n\u003cimg width=\"1704\" alt=\"image\" src=\"https://github.com/hrvadl/converter/assets/93580374/e026750c-b399-4801-acc2-76da4d316298\"\u003e\n\n## App diagram 🏛️\n\n\u003cimg width=\"864\" alt=\"image\" src=\"https://github.com/GenesisEducationKyiv/software-engineering-school-4-0-hrvadl/assets/93580374/3a7cc167-af07-4674-9c87-b35bb9f6f871\"\u003e\n\n## CI ⚙️\n\nThe application uses GI actions (free tear) as a CI runner. It suits perfectly for small non-commercial projects. CI heavily relies on the [taskfile](https://taskfile.dev/) to do its job. This means each CI step could easily be run locally. CI steps are run in parallel to reduce the time spent waiting for the results.\n\u003cimg width=\"1228\" alt=\"image\" src=\"https://github.com/hrvadl/converter/assets/93580374/77b9f5cf-1e9e-485f-a7f8-b29a092811f6\"\u003e\n\n## Per service documentation 📃\n\n- [Gateway](https://github.com/hrvadl/converter/blob/main/gw/README.md)\n- [Mailer](https://github.com/hrvadl/converter/blob/main/mailer/README.md)\n- [Ratewatcher](https://github.com/hrvadl/converter/blob/main/rw/README.md)\n- [Subscriber](https://github.com/hrvadl/converter/blob/main/sub/README.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhrvadl%2Fconverter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhrvadl%2Fconverter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhrvadl%2Fconverter/lists"}