From 53e35c726341422d8f3a366a8b2b5f6e1f906b73 Mon Sep 17 00:00:00 2001 From: Andrew nuark G Date: Tue, 16 Jun 2026 21:34:35 +0700 Subject: [PATCH] chore: copied WEB_API.md from, UpdateForge project for reference --- WEB_API.md | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 WEB_API.md diff --git a/WEB_API.md b/WEB_API.md new file mode 100644 index 0000000..a87cd7c --- /dev/null +++ b/WEB_API.md @@ -0,0 +1,198 @@ +# UpdateForge API + +Base URL: `https://` +All API routes are under `/api/v1`. +All request and response bodies are JSON. + +--- + +## Authentication + +UpdateForge uses **Bearer token** authentication. Obtain a token via the login endpoint and include it in every subsequent request: + +``` +Authorization: Bearer +``` + +Tokens are long-lived JWTs signed by the server. Store them securely (e.g. Android `EncryptedSharedPreferences`). There is no built-in expiry endpoint — simply re-authenticate when you receive a `401`. + +--- + +## Endpoints + +### `POST /api/v1/auth` + +Authenticate with email and password. Does not require an existing token. + +**Request** + +```json +{ + "email": "user@example.com", + "password": "secret" +} +``` + +**Response `200`** + +```json +{ + "token": "eyJ...", + "userId": "abc123", + "email": "user@example.com" +} +``` + +**Error responses** + +| Status | Body | Reason | +| ------ | ---------------------------------------------- | ----------------------- | +| `400` | `{"error": "email and password are required"}` | Missing fields | +| `401` | `{"error": "invalid credentials"}` | Wrong email or password | + +--- + +### `GET /api/v1/apps` + +Returns all teams the authenticated user belongs to, each with its apps. A user may belong to multiple teams. Within each app, the response includes the latest **current** and **pending** version per build type. + +- **current** — the highest semver version that has an artifact file attached. Always present (may be `null` if none exists yet). +- **pending** — the highest semver version with no artifact file yet (work in progress). `null` when none exists. + +Teams and apps are ordered oldest-first by creation date. + +**Response `200`** + +```json +{ + "teams": [ + { + "id": "team_id", + "name": "Acme", + "apps": [ + { + "id": "app_id", + "title": "My App", + "packageName": "com.example.myapp", + "icon": "/api/files/apps/app_id/icon.png", + "release": { + "current": { + "id": "ver_id", + "version": "1.2.0", + "name": "January update", + "public": true, + "hasFile": true, + "created": "2026-01-15T10:30:00Z" + }, + "pending": null + }, + "debug": { + "current": { + "id": "ver_id_2", + "version": "1.3.0", + "name": "", + "public": false, + "hasFile": true, + "created": "2026-01-20T08:00:00Z" + }, + "pending": { + "id": "ver_id_3", + "version": "1.4.0", + "name": "Next sprint", + "public": false, + "hasFile": false, + "created": "2026-01-21T09:00:00Z" + } + }, + "profile": { + "current": null, + "pending": null + } + } + ] + } + ] +} +``` + +`teams` is an empty array if the user belongs to no teams. + +**Version object fields** + +| Field | Type | Description | +| --------- | ------ | ------------------------------------------------------ | +| `id` | string | Version record ID — use this for downloads | +| `version` | string | Semantic version string, e.g. `"1.2.0"` | +| `name` | string | Optional human-readable label, empty string if not set | +| `public` | bool | Whether this version is intended for all testers | +| `hasFile` | bool | Whether an artifact is available for download | +| `created` | string | ISO 8601 UTC timestamp | + +**Error responses** + +| Status | Body | Reason | +| ------ | --------------------------- | ------------------------ | +| `401` | `{"error": "unauthorized"}` | Missing or invalid token | + +--- + +### `GET /api/v1/versions/{id}/download` + +Download the artifact file for a version. `{id}` is the version's `id` field from the apps response. + +The response is the raw file with `Content-Type: application/octet-stream` and a `Content-Disposition: attachment` header carrying the original filename. Supports HTTP range requests, so partial downloads and resume work out of the box. + +**Error responses** + +| Status | Body | Reason | +| ------ | -------------------------------- | ------------------------------------------ | +| `401` | `{"error": "unauthorized"}` | Missing or invalid token | +| `403` | `{"error": "forbidden"}` | User is not a member of the version's team | +| `404` | `{"error": "version not found"}` | ID does not exist | +| `404` | `{"error": "no file attached"}` | Version exists but `hasFile` is `false` | + +--- + +## Build types + +Every version belongs to exactly one build type: + +| Type | Intended use | +| --------- | ------------------------------------------- | +| `release` | Production-ready builds for general testers | +| `debug` | Debug builds with logging/tooling enabled | +| `profile` | Performance-profiling builds | + +--- + +## Icons + +The `icon` field in the app object is a root-relative URL path. Prepend the server base URL to fetch it: + +``` +GET https:///api/files/apps// +``` + +Icon files are served publicly and do not require authentication. + +--- + +## Typical client flow + +``` +1. POST /api/v1/auth → store token +2. GET /api/v1/apps → display teams and their apps; cache version IDs +3. For each app, compare installed version string with current.version +4. If update available and current.hasFile == true: + GET /api/v1/versions/{id}/download → save & install APK +``` + +--- + +## Error format + +All error responses share the same shape: + +```json +{ "error": "" } +```