{"id":24966096,"url":"https://github.com/rustam-tolipov/personal_budget_api_v2","last_synced_at":"2026-05-08T14:46:11.809Z","repository":{"id":301301331,"uuid":"655655723","full_name":"rustam-tolipov/personal_budget_api_v2","owner":"rustam-tolipov","description":"Discover our Personal Budget Planner on GitHub! Simplify your finances effortlessly with our easy-to-use app. Invite family members via email to join in tracking expenses together, promoting transparency and teamwork. No more tangled spreadsheets – our friendly interface makes budgeting a breeze.","archived":false,"fork":false,"pushed_at":"2026-04-21T11:41:29.000Z","size":1050,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"dev","last_synced_at":"2026-04-21T13:33:15.130Z","etag":null,"topics":["postgresql","ruby","ruby-on-rails"],"latest_commit_sha":null,"homepage":"https://github.com/rustam-tolipov/personal_budget_ui_v2","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rustam-tolipov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-06-19T10:28:15.000Z","updated_at":"2026-04-21T11:41:34.000Z","dependencies_parsed_at":"2025-06-26T07:02:08.804Z","dependency_job_id":"badb3e8d-6613-4fea-9b91-ed4e39c1799c","html_url":"https://github.com/rustam-tolipov/personal_budget_api_v2","commit_stats":null,"previous_names":["rustam-tolipov/personal_budget_api_v2"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rustam-tolipov/personal_budget_api_v2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustam-tolipov%2Fpersonal_budget_api_v2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustam-tolipov%2Fpersonal_budget_api_v2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustam-tolipov%2Fpersonal_budget_api_v2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustam-tolipov%2Fpersonal_budget_api_v2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rustam-tolipov","download_url":"https://codeload.github.com/rustam-tolipov/personal_budget_api_v2/tar.gz/refs/heads/dev","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustam-tolipov%2Fpersonal_budget_api_v2/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32785391,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"ssl_error","status_checked_at":"2026-05-08T08:22:45.650Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["postgresql","ruby","ruby-on-rails"],"created_at":"2025-02-03T11:18:37.940Z","updated_at":"2026-05-08T14:46:11.632Z","avatar_url":"https://github.com/rustam-tolipov.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv id=\"top\"\u003e\u003c/div\u003e\n\n[![Contributors][contributors-shield]][contributors-url]\n[![Forks][forks-shield]][forks-url]\n[![Stargazers][stars-shield]][stars-url]\n[![Issues][issues-shield]][issues-url]\n[![MIT License][license-shield]][license-url]\n\n\u003cbr /\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://github.com/Rustamxon7/personal_budget_api_v2\"\u003e\n    \u003cimg src=\"images/beedget.svg\" alt=\"Logo\" width=\"80\" height=\"80\"\u003e\n  \u003c/a\u003e\n\n  \u003ch3 align=\"center\"\u003ePersonal Budget Planner API\u003c/h3\u003e\n\n  \u003cp align=\"center\"\u003e\n    A Rails 7 JSON API for collaborative family budget tracking.\n    \u003cbr /\u003e\n    \u003ca href=\"https://github.com/Rustamxon7/personal_budget_api_v2/issues\"\u003eReport Bug\u003c/a\u003e\n    ·\n    \u003ca href=\"https://github.com/Rustamxon7/personal_budget_api_v2/issues\"\u003eRequest Feature\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n---\n\n## About\n\nA Rails 7 API-only backend for tracking personal and family finances. Users can create budget members (family participants), assign income/expense categories to them, and log transactions. Each write updates a cached `total_amounts` table to avoid recomputing balances on every read.\n\n**Frontend repo:** [personal_budget_ui_v2](https://github.com/Rustamxon7/personal_budget_ui_v2)\n\n---\n\n## Tech Stack\n\n- **Ruby** 3.3.6 / **Rails** 7.0\n- **PostgreSQL**\n- **Devise** + **devise-jwt** - JWT authentication (token in `Authorization: Bearer` header, 48h expiry)\n- **CarrierWave** + **Cloudinary** - avatar uploads\n- **Active Model Serializers** - JSON response shaping\n- **RSpec** - request and model specs\n- **Rswag** - Swagger API docs at `/`\n- **Fly.io** - deployment\n\n---\n\n## Getting Started\n\n### Prerequisites\n\n- Ruby 3.3.6\n- PostgreSQL running locally\n\n### Installation\n\n1. Clone the repo\n\n   ```sh\n   git clone https://github.com/Rustamxon7/personal_budget_api_v2.git\n   cd personal_budget_api_v2\n   ```\n\n2. Install dependencies\n\n   ```sh\n   bundle install\n   ```\n\n3. Set up environment variables\n\n   ```sh\n   cp .env.example .env\n   ```\n\n   Fill in your PostgreSQL credentials, Cloudinary keys, and a `SECRET_KEY_BASE` (generate one with `rails secret`).\n\n4. Set up the database\n\n   ```sh\n   rails db:reset\n   ```\n\n5. Start the server\n\n   ```sh\n   rails s\n   ```\n\n---\n\n## Authentication\n\nAuthentication uses Devise with JWT tokens.\n\n- **Sign up:** `POST /api/v1/auth/signup`\n- **Log in:** `POST /api/v1/auth/login` - returns a JWT token in the response\n- **Log out:** `DELETE /api/v1/auth/logout`\n\nPass the token on all subsequent requests:\n\n```\nAuthorization: Bearer \u003ctoken\u003e\n```\n\nTokens expire after 48 hours. There is currently no token denylist - logging out does not invalidate the token server-side. See [Known Limitations](#known-limitations).\n\n---\n\n## Data Model\n\n```\nUser\n └── Members (budget participants)\n      └── Categories (income or expense)\n           └── Transactions\n                └── TotalAmounts (cached balance per category + member)\n```\n\n- A `User` auto-creates a default `Member` on registration\n- `Categories` are shared across members via a join table\n- Each `Transaction` write updates the corresponding `TotalAmount` record inside a database transaction block\n\n---\n\n## Running Tests\n\n```sh\nbundle exec rspec\n```\n\n---\n\n## Known Limitations\n\nThese are documented gaps - not bugs to work around, but areas planned for future improvement:\n\n- **No authorization layer** - any authenticated user can read or modify any other user's data. Ownership checks are not enforced beyond `authenticate_user!`.\n- **No JWT token denylist** - logging out does not revoke the token server-side. A stolen token remains valid until expiry.\n- **CarrierWave** - planned replacement with ActiveStorage.\n- **Active Model Serializers** - planned replacement with jsonapi-serializer.\n\n---\n\n## License\n\nDistributed under the MIT License. See `LICENSE` for more information.\n\n---\n\n## Contact\n\nRustam Tolipov - rustamtolipov.dev@gmail.com\n\nProject Link: [https://github.com/Rustamxon7/personal_budget_api_v2](https://github.com/Rustamxon7/personal_budget_api_v2)\n\n---\n\n\u003c!-- MARKDOWN LINKS --\u003e\n[contributors-shield]: https://img.shields.io/github/contributors/Rustamxon7/personal_budget_api_v2.svg?style=for-the-badge\n[contributors-url]: https://github.com/Rustamxon7/personal_budget_api_v2/graphs/contributors\n[forks-shield]: https://img.shields.io/github/forks/Rustamxon7/personal_budget_api_v2.svg?style=for-the-badge\n[forks-url]: https://github.com/Rustamxon7/personal_budget_api_v2/network/members\n[stars-shield]: https://img.shields.io/github/stars/Rustamxon7/personal_budget_api_v2.svg?style=for-the-badge\n[stars-url]: https://github.com/Rustamxon7/personal_budget_api_v2/stargazers\n[issues-shield]: https://img.shields.io/github/issues/Rustamxon7/personal_budget_api_v2.svg?style=for-the-badge\n[issues-url]: https://github.com/Rustamxon7/personal_budget_api_v2/issues\n[license-shield]: https://img.shields.io/github/license/Rustamxon7/personal_budget_api_v2.svg?style=for-the-badge\n[license-url]: https://github.com/Rustamxon7/personal_budget_api_v2/blob/master/LICENSE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frustam-tolipov%2Fpersonal_budget_api_v2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frustam-tolipov%2Fpersonal_budget_api_v2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frustam-tolipov%2Fpersonal_budget_api_v2/lists"}