{"id":51115163,"url":"https://github.com/faisalaffan/community-waste-collection-api","last_synced_at":"2026-06-24T21:01:15.425Z","repository":{"id":365390748,"uuid":"1271496373","full_name":"faisalaffan/community-waste-collection-api","owner":"faisalaffan","description":"REST API for community waste collection — households, pickups, payments, and reports. Built with Go Fiber","archived":false,"fork":false,"pushed_at":"2026-06-17T06:15:22.000Z","size":3629,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"dev","last_synced_at":"2026-06-17T07:15:56.445Z","etag":null,"topics":["atlas","docker","fiber","ghcr","go","golang","gorm","minio","openapi","postgresql","rest-api","swagger","waste-management"],"latest_commit_sha":null,"homepage":"https://waste-collection.faisalaffan.com","language":"Go","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/faisalaffan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":"CODEOWNERS","security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":null,"authors":"AUTHORS.md","dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["faisalaffan"],"custom":["https://saweria.co/faisalaffan"]}},"created_at":"2026-06-16T18:10:38.000Z","updated_at":"2026-06-17T06:15:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/faisalaffan/community-waste-collection-api","commit_stats":null,"previous_names":["faisalaffan/community-waste-collection-api"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/faisalaffan/community-waste-collection-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faisalaffan%2Fcommunity-waste-collection-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faisalaffan%2Fcommunity-waste-collection-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faisalaffan%2Fcommunity-waste-collection-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faisalaffan%2Fcommunity-waste-collection-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/faisalaffan","download_url":"https://codeload.github.com/faisalaffan/community-waste-collection-api/tar.gz/refs/heads/dev","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faisalaffan%2Fcommunity-waste-collection-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34749211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-24T02:00:07.484Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["atlas","docker","fiber","ghcr","go","golang","gorm","minio","openapi","postgresql","rest-api","swagger","waste-management"],"created_at":"2026-06-24T21:01:15.109Z","updated_at":"2026-06-24T21:01:15.371Z","avatar_url":"https://github.com/faisalaffan.png","language":"Go","funding_links":["https://github.com/sponsors/faisalaffan","https://saweria.co/faisalaffan"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/02_BANNERS.png\" alt=\"Community Waste Collection API\" width=\"100%\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/01_LOGO.png\" alt=\"Logo\" width=\"120\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eCommunity Waste Collection API\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003eREST API + Admin Dashboard for community waste collection — households, pickups, payments, and reports.\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/faisalaffan/community-waste-collection-api/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/faisalaffan/community-waste-collection-api/actions/workflows/ci.yml/badge.svg\" alt=\"CI\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/faisalaffan/community-waste-collection-api/actions/workflows/release.yml\"\u003e\u003cimg src=\"https://github.com/faisalaffan/community-waste-collection-api/actions/workflows/release.yml/badge.svg\" alt=\"Release\" /\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/go-1.26-00ADD8?logo=go\" alt=\"Go\" /\u003e\n  \u003ca href=\"https://codecov.io/gh/faisalaffan/community-waste-collection-api\"\u003e\u003cimg src=\"https://codecov.io/gh/faisalaffan/community-waste-collection-api/branch/dev/graph/badge.svg\" alt=\"Coverage\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/faisalaffan/community-waste-collection-api/pkgs/container/community-waste-collection-api\"\u003e\u003cimg src=\"https://img.shields.io/badge/ghcr-v1.0.0-blue?logo=docker\" alt=\"GHCR\" /\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/license-MIT-green\" alt=\"License\" /\u003e\n\u003c/p\u003e\n\n## Screenshots\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"screenshot/01_DASHBOARD.png\" alt=\"Dashboard\" width=\"48%\" /\u003e\n  \u003cimg src=\"screenshot/02_HOUSEHOLD.png\" alt=\"Households\" width=\"48%\" /\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"screenshot/03_PICKUP.png\" alt=\"Pickups\" width=\"48%\" /\u003e\n  \u003cimg src=\"screenshot/04_PAYMENT.png\" alt=\"Payments\" width=\"48%\" /\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"screenshot/05_REPORT.png\" alt=\"Reports\" width=\"48%\" /\u003e\n  \u003cimg src=\"screenshot/07_SWAGGER.png\" alt=\"Swagger\" width=\"48%\" /\u003e\n\u003c/p\u003e\n\n## Coverage\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/03_FULL_COVERAGE.png\" alt=\"100% Coverage\" width=\"70%\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003e243 tests — 100% statement coverage\u003c/strong\u003e across all 11 packages.\u003cbr /\u003e\n  Every repository, service, handler, middleware, storage, and router function is tested.\u003cbr /\u003e\n  \u003cem\u003eCovered: config, handler, middleware, repository, router, service, worker, database, response, storage.\u003c/em\u003e\n\u003c/p\u003e\n\n## Architecture\n\n```\ncmd/server/          Entry point, DI wiring, graceful shutdown\ninternal/\n  config/            Viper .env loader\n  domain/            GORM models, DTOs, type constants\n  handler/           HTTP handlers (Fiber v3)\n  service/           Business logic\n  repository/        Data access (GORM)\n  middleware/         Rate limiter\n  router/            Routes + Swagger UI\n  worker/            Background goroutines\npkg/\n  database/          GORM connection helper\n  storage/           MinIO/S3 client\n  response/          JSON response envelope\nmigrations/          Atlas HCL schema + config\ndocs/                Swagger spec\nweb/                 Vue 3 + Tailwind SPA\nassets/              Logo + banner\n```\n\n**Stack**: Go 1.26 · Fiber v3 · GORM · PostgreSQL 16 · Atlas · Viper · MinIO · Docker · Vue 3 CDN · Tailwind CDN\n\n## Technical Decisions\n\nSetiap keputusan teknis di project ini punya alasan — bukan sekadar preferensi. Dokumen lengkap: **[docs/technical-decisions.md](docs/technical-decisions.md)**.\n\n| Decision | Why |\n|---|---|\n| **Go + Fiber v3** | Fasthttp-based, throughput 2-10x Gin, zero alloc router |\n| **Clean Architecture** | Handler → Service → Repository: testable, replaceable, no circular deps |\n| **GORM** | Multi-driver (SQLite test / PostgreSQL prod), auto-migration, less boilerplate |\n| **PostgreSQL + UUID PK** | Distributed-safe, no ID collision, compatible with horizontal scaling |\n| **Atlas (declarative)** | Write desired state, not migration steps — safety with `--dry-run` |\n| **MinIO / S3** | Stateless app, same API dev→prod, DB not burdened with binary data |\n| **Docker multi-service** | Production parity, health-check gated startup, volume persistence |\n| **Vue 3 + Tailwind CDN** | Zero build step, no `node_modules`, Docker image stays ~30MB |\n| **Background goroutine** | Self-contained worker, graceful via context, no external scheduler |\n| **Manual DI** | Explicit dependency graph, compile-time safety, no code generation |\n| **Envelope response** | Client-side consistency — single `status` check for all API outcomes |\n| **243 tests · 100% coverage** | SQLite in-memory driver, mock-ready interfaces, layer isolation |\n\n## Quick Start\n\nPrerequisites: Go 1.26+, Docker, [Atlas CLI](https://atlasgo.io/getting-started).\n\n```bash\ngit clone https://github.com/faisalaffan/community-waste-collection-api.git\ncd community-waste-collection-api\ncp .env.example .env\nmake docker-up\nmake schema-apply\n```\n\nAdmin Dashboard → `http://localhost:8080` · Swagger → `http://localhost:8080/swagger` · Postman → [Collection](assets/postman_collection.json)\n\n**Live Demo**: [waste-collection.faisalaffan.com](https://waste-collection.faisalaffan.com) · [Swagger API](https://waste-collection.faisalaffan.com/swagger)\n\n## Video Tutorial\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.tella.tv/video/faisal-affan-backend-engineer-english-4ogm\"\u003e\n    \u003cimg src=\"assets/og.png\" alt=\"Video Tutorial\" width=\"600\" style=\"border-radius: 12px\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003e\u003ca href=\"https://www.tella.tv/video/faisal-affan-backend-engineer-english-4ogm\"\u003eWatch Video Tutorial →\u003c/a\u003e\u003c/strong\u003e\u003cbr /\u003e\n  \u003cem\u003eArchitecture walkthrough, API demo, deployment guide.\u003c/em\u003e\n\u003c/p\u003e\n\n## API Reference\n\nBase: `/api`. Response envelope:\n\n- **Success**: `{\"status\":\"success\",\"data\":{}}`\n- **List**: `{\"status\":\"success\",\"data\":[],\"pagination\":{\"page\":1,\"per_page\":10,\"total\":42,\"total_pages\":5}}`\n- **Error**: `{\"status\":\"error\",\"error\":{\"code\":\"NOT_FOUND\",\"message\":\"...\"}}`\n- **Validation**: `{\"status\":\"fail\",\"error\":{\"code\":\"VALIDATION_ERROR\",\"message\":\"...\",\"details\":[]}}`\n\n### Households\n\n```\nPOST   /api/households        Create  {owner_name, address}\nGET    /api/households        List    ?page=1\u0026per_page=10\nGET    /api/households/:id    Get\nPUT    /api/households/:id    Update  {owner_name, address}\nDELETE /api/households/:id    Delete\n```\n\n### Waste Pickups\n\n```\nPOST   /api/pickups               Create   {household_id, type, safety_check}\nGET    /api/pickups               List     ?status=\u0026household_id=\u0026page=1\u0026per_page=10\nPUT    /api/pickups/:id           Update   {type, safety_check}\nDELETE /api/pickups/:id           Delete\nPUT    /api/pickups/:id/schedule  Schedule {pickup_date}\nPUT    /api/pickups/:id/complete  Complete  (auto-generates payment)\nPUT    /api/pickups/:id/cancel    Cancel\n```\n\nTypes: `organic`, `plastic`, `paper`, `electronic`. Rate limit: 30 req/min.\n\n### Payments\n\n```\nPOST /api/payments              Create\nGET  /api/payments              List     ?status=\u0026household_id=\u0026page=1\u0026per_page=10\nPUT  /api/payments/:id/confirm  Confirm  (multipart: proof_file)\n```\n\n### Reports\n\n```\nGET /api/reports/waste-summary           Pickup aggregation by type\nGET /api/reports/payment-summary         Payment totals (pending, paid, failed) + revenue\nGET /api/reports/households/:id/history  Pickup + payment history per household\n```\n\n### Files\n\n```\nGET /api/files/proof/:paymentID   Serve payment proof image (proxied from S3)\n```\n\n## Business Rules\n\n- Pending payment → blocked from new pickup (409)\n- Only `pending` pickups can be scheduled (409)\n- Electronic waste requires `safety_check: true` (422)\n- Organic pickups pending \u003e3 days auto-canceled\n- Completing pickup auto-generates payment (Rp 50k organic/plastic/paper, Rp 100k electronic)\n- Payment confirmation requires S3/MinIO proof upload\n\n## Database\n\nSchema managed by [Atlas](https://atlasgo.io) (`migrations/schema.pg.hcl`).\n\n```bash\nmake schema-apply      # Apply\nmake schema-diff       # Preview (dry-run)\nmake schema-inspect    # Pull from DB\nmake schema-dump       # Export DDL to migrations/schema.sql\n```\n\nTables: `households`, `waste_pickups`, `payments` — UUID PKs, FK with CASCADE, B-tree indexes.\n\n## Testing\n\n243 tests · 100% coverage.\n\n```bash\nmake test              # go test ./... -v\nmake coverage          # profile + summary\nmake coverage-html     # browser report\n```\n\n## Makefile\n\n```bash\nmake build             # Build binary\nmake dev               # go run\nmake test              # Run tests\nmake lint              # go vet\nmake docker-up         # Start all services\nmake docker-down       # Stop all services\nmake swagger-clean     # Regenerate swagger\nmake schema-apply      # Apply Atlas schema\nmake schema-dump       # Export DDL to SQL\nmake all               # lint + test + build\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaisalaffan%2Fcommunity-waste-collection-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffaisalaffan%2Fcommunity-waste-collection-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaisalaffan%2Fcommunity-waste-collection-api/lists"}