{"id":51106191,"url":"https://github.com/ilramdhan/fullstack-go-vue-assesement","last_synced_at":"2026-06-24T14:01:37.549Z","repository":{"id":354813552,"uuid":"1225323979","full_name":"ilramdhan/fullstack-go-vue-assesement","owner":"ilramdhan","description":"Submission Payment Dashboard - Internal dashboard for monitoring and reviewing incoming payments. Built for the Durianpay Full Stack Engineer take-home assignment.","archived":false,"fork":false,"pushed_at":"2026-05-01T09:22:37.000Z","size":230,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-06-22T15:38:01.459Z","etag":null,"topics":["chi-router","go","golang","jwt","openai","pinia","shadcn-vue","sqlite","tailwindcss","tanstack-query","typescript","vite","vue3"],"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/ilramdhan.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-30T07:03:36.000Z","updated_at":"2026-06-13T14:15:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ilramdhan/fullstack-go-vue-assesement","commit_stats":null,"previous_names":["ilramdhan/fullstack-go-vue-assesement"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ilramdhan/fullstack-go-vue-assesement","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilramdhan%2Ffullstack-go-vue-assesement","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilramdhan%2Ffullstack-go-vue-assesement/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilramdhan%2Ffullstack-go-vue-assesement/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilramdhan%2Ffullstack-go-vue-assesement/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ilramdhan","download_url":"https://codeload.github.com/ilramdhan/fullstack-go-vue-assesement/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilramdhan%2Ffullstack-go-vue-assesement/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34735266,"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":["chi-router","go","golang","jwt","openai","pinia","shadcn-vue","sqlite","tailwindcss","tanstack-query","typescript","vite","vue3"],"created_at":"2026-06-24T14:01:35.234Z","updated_at":"2026-06-24T14:01:37.536Z","avatar_url":"https://github.com/ilramdhan.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Payment Dashboard\n\nInternal dashboard for monitoring and reviewing incoming payments. Built for the\nDurianpay Full Stack Engineer take-home assignment.\n\n- **Backend** — Go 1.22, chi router, SQLite (WAL), JWT auth, OpenAPI-first.\n- **Frontend** — Vue 3 + TypeScript + Vite, Pinia, TanStack Vue Query, shadcn-vue + Tailwind 3.\n- **Contract** — `openapi.yaml` at the repo root drives both the Go server stubs and the typed FE client.\n\n```\n.\n├── openapi.yaml                # single source of truth for the HTTP API\n├── backend/                    # Go service\n├── frontend/                   # Vue app\n├── docker-compose.yml          # one-command bootstrap (BE + FE)\n├── Makefile                    # stack-level targets (up/down/test/...)\n└── README.md\n```\n\n## 1. Prerequisites\n\n| Tool | Version | Used for |\n|---|---|---|\n| Docker + Docker Compose | any recent | the recommended path; runs both services |\n| Go | 1.21+ | backend (only if running locally without Docker) |\n| Node.js | 20+ | frontend (only if running locally without Docker) |\n| GNU Make | any | shortcut targets |\n\nThe reviewer environment described in the assignment (Go 1.21+, Node 20+, Docker, Docker Compose, Make, macOS) is fully covered.\n\n## 2. Quick start (Docker)\n\n```bash\ngit clone \u003cthis-repo\u003e payment-dashboard\ncd payment-dashboard\n\nmake up                           # builds + starts BE and FE\n```\n\nThen open:\n\n| URL | What |\n|---|---|\n| \u003chttp://localhost:8088\u003e | Frontend dashboard |\n| \u003chttp://localhost:8080/healthz\u003e | Backend liveness probe |\n| \u003chttp://localhost:8080/swagger\u003e | Swagger UI for the OpenAPI contract |\n| \u003chttp://localhost:8080/openapi.json\u003e | Raw OpenAPI 3.0.3 spec |\n\nWhen you're done:\n\n```bash\nmake down        # stops the stack, keeps the sqlite volume\nmake clean       # stops + drops the volume (database is reset on next up)\nmake logs        # tail logs from both services\n```\n\n### Demo accounts\n\nThe first time the backend boots it migrates the schema and seeds two users plus 50 demo payments (38 completed / 7 processing / 5 failed).\n\n| Email | Password | Role | Capabilities |\n|---|---|---|---|\n| `cs@test.com` | `password` | `cs` | Read-only |\n| `operation@test.com` | `password` | `operation` | Read + approve/reject `processing` payments |\n\n## 3. Manual setup (without Docker)\n\nTwo terminals, run from the repo root.\n\n**Backend** (terminal 1):\n\n```bash\ncd backend\ncp env.sample .env             # adjust JWT_SECRET if you like\nmake tool-openapi              # one-time: install oapi-codegen\nmake openapi-gen               # regenerate openapigen package from ../openapi.yaml\nmake dep                       # go mod tidy\nmake run                       # listens on :8080\n```\n\n**Frontend** (terminal 2):\n\n```bash\ncd frontend\ncp .env.example .env           # contains VITE_API_BASE_URL=http://localhost:8080\nnpm install\nnpm run dev                    # listens on :5173, proxies /api -\u003e :8080\n```\n\nOpen \u003chttp://localhost:5173\u003e.\n\nFor a production build of the frontend: `npm run build \u0026\u0026 npm run preview` (port 4173).\n\n## 4. API contract\n\nThe contract lives in [`openapi.yaml`](./openapi.yaml). Both the Go server stubs (`backend/internal/openapigen`) and the typed TS client (`frontend/src/api/generated`) are regenerated from it.\n\n| Method | Path | Auth | Notes |\n|---|---|---|---|\n| `POST` | `/dashboard/v1/auth/login` | public | email + password → JWT + user (email, role) |\n| `GET`  | `/dashboard/v1/payments` | bearer | filter by `status`, `id`, sort, paginate |\n| `GET`  | `/dashboard/v1/payments/summary` | bearer | aggregated counts by status |\n| `PUT`  | `/dashboard/v1/payments/{id}/review` | bearer + role `operation` | approve/reject a `processing` payment |\n| `GET`  | `/healthz` | public | liveness probe |\n| `GET`  | `/swagger` | public | Swagger UI for interactive exploration |\n\nThe OpenAPI request validator is mounted as middleware, so any request that violates the contract (bad enum, missing field, wrong type) is rejected with `400` before reaching a handler.\n\n## 5. Testing strategy\n\nBoth layers have unit + integration coverage and lint passes are required for the `make test` target to succeed.\n\n### Backend (`backend/Makefile`)\n\n```bash\nmake -C backend test          # plain run\nmake -C backend test-race     # race detector\nmake -C backend test-cover    # writes coverage.html\n```\n\n- **Repository tests** — sqlite `:memory:` with the real migration runner; covers every WHERE/ORDER BY branch and a `SQL-injection-shaped sort value` is asserted to fall back to the safe default.\n- **Usecase tests** — interface-driven stub repositories; covers JWT issue + verify roundtrip, expired tokens, signature rejection, the review state-machine, validation failures.\n- **Middleware tests** — `httptest` request recorder; covers Bearer parser edge cases (missing/malformed/empty) and role-guard allowed/denied paths.\n- **HTTP integration tests** — full chi pipeline (`OpenAPI validator → conditionalAuth → handler`) wired against an in-memory db with seeded fixtures; one happy path per endpoint plus every status code we promise: `200/400/401/403/404/409`.\n\nStatements coverage on `auth + payment + middleware` packages: **84.8%**, all green under `-race`.\n\n### Frontend (`frontend/package.json`)\n\n```bash\ncd frontend\nnpm test                      # vitest run\nnpm run test:cover            # vitest run --coverage\n```\n\n- **MSW at the network layer** — `msw/node` handlers stand in for the real backend, so the same axios + `@tanstack/vue-query` + hey-api SDK pipeline runs end-to-end. Handlers are stateful (`approve` mutates the in-memory list) so optimistic updates and cache invalidation are exercised.\n- **Component tests** with `@testing-library/vue` and a `renderWithStack` helper that mounts Pinia, Vue Query, and Vue Router exactly the way `main.ts` does in production.\n- **Pure logic tests** for `formatCurrency`/`formatDate`, the auth store (set / clear / persist / rehydrate / corrupt-storage tolerance), and the router guards.\n\nStatements coverage: 78.2% overall, **91.1% on `features/auth`**, **80.4% on `features/payments`**, 100% on `lib/format`.\n\n## 6. Environment variables\n\nBackend (`backend/env.sample`):\n\n| Var | Default | Purpose |\n|---|---|---|\n| `HTTP_ADDR` | `:8080` | Listen address |\n| `DATABASE_PATH` | `data/dashboard.db` | SQLite file (Docker: `/app/data/dashboard.db` on a named volume) |\n| `JWT_SECRET` | dev placeholder | HS256 signing key — replace before any real deploy |\n| `JWT_EXPIRED` | `24h` | Token TTL (Go duration) |\n| `CORS_ALLOWED_ORIGINS` | `http://localhost:5173,http://localhost:4173,http://localhost:3000` | CSV of allowed origins |\n\nFrontend (`frontend/.env.example`):\n\n| Var | Default | Purpose |\n|---|---|---|\n| `VITE_API_BASE_URL` | `http://localhost:8080` | Where the SPA looks for the backend |\n\nIn Docker the frontend image is built with `VITE_API_BASE_URL=/api` so the SPA talks to nginx, which proxies `/api/*` to the backend service over the Docker network.\n\n## 7. Architecture overview\n\n```\nBrowser ──► nginx (FE container) ──► Go server (BE container) ──► SQLite (volume)\n              │  /            (SPA + assets, gzip, SPA fallback)\n              └─ /api/*       (proxy_pass http://backend:8080/)\n```\n\nBackend is layered (entity → repository → usecase → handler → API adapter), wired manually in `main.go` with no DI framework. The OpenAPI request validator runs as the outer middleware on the protected route group, then a JWT auth middleware injects the verified identity into the request context, then the route-specific role check (for `PUT /payments/{id}/review`) runs inside the handler.\n\nFrontend splits state cleanly: **Pinia** owns client state (auth token + role + email, persisted to localStorage), **Vue Query** owns server state (payments list/summary/mutations) with optimistic updates and rollback. UI composes shadcn-vue primitives styled with Tailwind.\n\nSee `backend/README.md` and `frontend/README.md` for per-package detail.\n\n## 8. Troubleshooting\n\n- **Port already in use** — another process is on `:8080` or `:8088`. `lsof -nP -i :8080` to find it; either stop the other process or override the port mapping in `docker-compose.yml`.\n- **Backend container marked unhealthy** — `make logs` to see the error. The volume mount path `./backend/data` is created with the container's user; if you've previously created `backend/data` as root locally, `sudo chown -R $(id -u):$(id -g) backend/data`.\n- **`make up` fails on an arm64 Mac** — the backend builds CGO with the SQLite driver, which needs `gcc/musl-dev` in the builder stage; the Dockerfile already installs them. If the build fails on a fresh Mac, run `docker buildx build --platform linux/amd64` or update Docker Desktop.\n- **CORS error in the browser** — make sure `CORS_ALLOWED_ORIGINS` on the backend matches the origin you're loading the frontend from. Default covers `:5173`, `:4173`, and `:3000`.\n- **\"Session expired\" toast on every page** — your JWT_SECRET changed since the token was issued. Sign out and back in, or clear `localStorage` for the dashboard origin.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filramdhan%2Ffullstack-go-vue-assesement","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Filramdhan%2Ffullstack-go-vue-assesement","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filramdhan%2Ffullstack-go-vue-assesement/lists"}