From 43f1b29aee7b3cf2714633490b7e56c243cf6041 Mon Sep 17 00:00:00 2001 From: Amirhossein Khalili Date: Mon, 21 Sep 2026 17:57:11 +0330 Subject: [PATCH] docs(readme): streamline project README --- README.md | 146 +++++++++++++----------------------------------------- 1 file changed, 35 insertions(+), 111 deletions(-) diff --git a/README.md b/README.md index 78c826a..6e514a0 100644 --- a/README.md +++ b/README.md @@ -1,94 +1,59 @@ # Card to IBAN -A small full-stack application that converts an Iranian bank card number to its IBAN using the ZarinHub API. The backend is written in Go with the standard HTTP library, while the frontend uses Nuxt, Vue, and Tailwind CSS. +A small full-stack application that converts an Iranian bank card number to its IBAN through ZarinHub. -The request flow is intentionally simple: +- Backend: Go, `net/http`, SQLite +- Frontend: Nuxt, Vue, Tailwind CSS +- API documentation: OpenAPI 3.0 and Swagger UI -```text -Nuxt UI -> HTTP handler -> Card service -> ZarinHub client - | - -> Audit repository -> SQLite -``` +## Quick start with Docker -The HTTP handler coordinates the public API contract and audit persistence. The service validates card numbers, the ZarinHub client handles external requests, and the repository contains the SQLite-specific implementation. - -## Prerequisites - -- Go 1.27 or later -- Node.js 22 or later -- ZarinHub API credentials - -## Configuration - -Copy the example environment file from the project root: +Copy the environment template and add your ZarinHub API credentials: ```powershell Copy-Item .env.sample .env ``` -Set `ZARINHUB_API_USERNAME` and `ZARINHUB_API_PASSWORD` in the new `.env` file. These are the API credentials created in the ZarinHub panel, not the regular panel login credentials. The `.env` file is ignored by Git. - -| Variable | Default | Purpose | -| --- | --- | --- | -| `ZARINHUB_API_USERNAME` | None; required | API username created in the ZarinHub panel | -| `ZARINHUB_API_PASSWORD` | None; required | API password created in the ZarinHub panel | -| `ZARINHUB_AUTH_ENDPOINT` | Official GetToken endpoint | Token acquisition and refresh endpoint | -| `ZARINHUB_ENDPOINT` | Official CardToIban endpoint | External service endpoint | -| `ZARINHUB_TIMEOUT` | `5s` | External request timeout | -| `DATABASE_PATH` | `./card-to-iban.db` | SQLite database file path | -| `SERVER_ADDR` | `127.0.0.1:8080` | Backend HTTP address | -| `FRONTEND_ORIGIN` | `http://127.0.0.1:3000` | Allowed frontend origin | - -The frontend reads the following value from `frontend/.env` and uses the shown address by default: - ```env -NUXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8080 +ZARINHUB_API_USERNAME=your-api-username +ZARINHUB_API_PASSWORD=your-api-password ``` -The backend obtains an access token during startup, keeps the access and refresh tokens in memory, and refreshes them automatically shortly before expiration. Credentials and tokens are never written to the database or logs. +Build and start the application: -## Running the Project +```powershell +docker compose up --build +``` -Start the backend from the Go module directory. The root `.env` file is loaded automatically, and the database migration runs during startup: +Open: + +- App: `http://127.0.0.1:3000` +- Health check: `http://127.0.0.1:8080/health` +- Swagger UI: `http://127.0.0.1:8080/docs` +- OpenAPI document: `http://127.0.0.1:8080/openapi.json` + +After backend changes, restart it or run `docker compose up --build` again so the running container uses the latest code. + +## Local development + +Start the backend: ```powershell cd backend go run ./cmd/api ``` -In another terminal, install the frontend dependencies and start the development server: +Start the frontend in another terminal: ```powershell cd frontend -npm install +npm ci npm run dev ``` -The frontend is available at `http://127.0.0.1:3000`. The backend health endpoint is available at `http://127.0.0.1:8080/health`, and the interactive API documentation is available at `http://127.0.0.1:8080/docs`. +The backend loads `.env` from the project root. The frontend uses `http://127.0.0.1:8080` by default; override it with `NUXT_PUBLIC_API_BASE_URL` when needed. -## Running with Docker Compose - -Create the root `.env` file as described in the configuration section, then build and start both services: - -```powershell -docker compose up --build -``` - -Open `http://localhost:3000` in your browser. The API is exposed at `http://localhost:8080`, and SQLite data is kept in the named `card_to_iban_data` volume. - -Stop the services with: - -```powershell -docker compose down -``` - -The database volume is preserved by default. To remove it intentionally, run `docker compose down --volumes`. - -## API Contract - -The OpenAPI 3.0 document is served at `http://127.0.0.1:8080/openapi.json`. Swagger UI is available at `http://127.0.0.1:8080/swagger/`, with `/docs` provided as a shortcut. The OpenAPI document is embedded in the backend binary; only the Swagger UI assets are loaded from a CDN. - -### Convert a card number to an IBAN +## API ```http POST /api/v1/card-to-iban @@ -108,44 +73,16 @@ Successful response: } ``` -All errors use the same response structure. Error messages are returned in Persian to match the user interface: +Errors use the same `requestId` returned in the `X-Request-ID` header. See Swagger UI for the complete request, response, and error contract. -```json -{ - "error": { - "code": "invalid_card", - "message": "شماره کارت باید شامل ۱۶ رقم باشد." - }, - "requestId": "SERVER_GENERATED_REQUEST_ID" -} -``` +## Implementation notes -The same request ID is also returned in the `X-Request-ID` response header. +- The backend validates card numbers and refreshes ZarinHub access tokens automatically. +- Every conversion attempt is audited in SQLite. +- Card numbers and upstream responses are sanitized before storage; credentials and tokens are not persisted. +- SQLite data is stored in the `card_to_iban_data` Docker volume when using Compose. -| HTTP status | Stable error code | Meaning | -| --- | --- | --- | -| `400` | `invalid_card` | The JSON body or card number format is invalid | -| `405` | `method_not_allowed` | The request method is not `POST` | -| `422` | `conversion_rejected` | ZarinHub rejected the conversion | -| `502` | `upstream_failure` | ZarinHub authentication failed or its response was invalid | -| `503` | `upstream_unavailable` | The upstream service is temporarily unavailable | -| `504` | `upstream_timeout` | The upstream request timed out | -| `500` | `internal_error` | Audit persistence or another internal operation failed | - -## Auditing and Data Security - -- Every conversion attempt creates one audit record, including invalid requests and upstream failures. -- A response is returned only after its audit record is saved successfully. A database failure produces a generic `500` response. -- Full card numbers are never written to the database or logs. Stored values are masked, for example `603799******5678`. -- ZarinHub JSON responses are sanitized recursively before storage, and every field named `card` is masked. -- Non-JSON upstream responses are not stored because their contents cannot be safely verified. -- Client IP addresses are intentionally not collected, so the related audit column remains empty. -- Structured logs contain only the request ID, route, HTTP status, audit outcome, and duration. -- Request bodies are limited to 1 KiB, and ZarinHub response bodies are limited to 1 MiB. - -## Testing - -Backend tests do not use real credentials. They use fakes and `httptest` for ZarinHub requests: +## Tests ```powershell cd backend @@ -153,21 +90,8 @@ go test ./... go vet ./... ``` -Run the frontend tests and production build with: - ```powershell cd frontend npm test npm run build ``` - -The test suite covers card validation and masking, the conversion service, the ZarinHub client, response-size limits, the SQLite repository, JSON sanitization, HTTP outcomes, audit failures, Persian and Arabic digit normalization, and frontend success and error states. - -## Assumptions and Limitations - -- Backend validation intentionally accepts only exactly 16 ASCII digits. Luhn validation is outside the assessment requirements. -- The frontend normalizes Persian and Arabic digits and removes spaces or hyphens before submission. -- SQLite is sufficient for this assessment and uses a single writer connection for predictable behavior. -- Only the IBAN is exposed to the frontend. Account details, owner information, and raw ZarinHub responses remain internal. -- Retries, CI, dark mode, and production infrastructure are intentionally outside the project scope. -- The interface uses the existing ZarinPal design system's font and logo assets. The font license is stored in `frontend/public/fonts`.