diff --git a/docs/api/README.md b/docs/api/README.md index e69a22b..0f65c71 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -3,6 +3,7 @@ ``` http://localhost:4208/api https://localhost:8443/api +https://localhost:8443/v1 (POS API) ``` ## CORS & cookies @@ -154,6 +155,7 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route | [`routes/unit/delete.md`](routes/unit/delete.md) | `DELETE /unit` | | [`routes/unit/list.md`](routes/unit/list.md) | `GET /unit` | | [`routes/unit/upsert.md`](routes/unit/upsert.md) | `PUT /unit` | +| [`routes/pos/client.md`](routes/pos/client.md) | `GET /v1/client` | | [`routes/warehouse/list.md`](routes/warehouse/list.md) | `GET /warehouse` | | [`routes/warehouse/create.md`](routes/warehouse/create.md) | `POST /warehouse` | | [`routes/warehouse/order.md`](routes/warehouse/order.md) | `PUT /warehouse/order` | diff --git a/docs/api/openapi.json b/docs/api/openapi.json index 9f0f06d..cedfccc 100644 --- a/docs/api/openapi.json +++ b/docs/api/openapi.json @@ -8,7 +8,11 @@ "servers": [ { "url": "http://localhost:4208/api", - "description": "Local default (PORT / .env)" + "description": "Local default HTTP (PORT / .env)" + }, + { + "url": "https://localhost:8443", + "description": "Local default HTTPS (HTTPS_PORT / .env)" } ], "tags": [ @@ -20,6 +24,10 @@ "name": "Health", "description": "Connectivity checks" }, + { + "name": "POS", + "description": "POS bind and sync API (/v1)" + }, { "name": "Events", "description": "SSE update signals for live clients" @@ -11932,6 +11940,114 @@ } } } + }, + "/v1/client": { + "get": { + "tags": [ + "POS" + ], + "summary": "POS discovery and pairing", + "description": "Special API for POS bind and sync.\n- Step 1 (Discovery): `authCode` length 1–4. Returns certificate fingerprints, serial number, and mandantId.\n- Step 2 (Pairing): `authCode` length 6 (pairingpin from posterminal). Verifies the pairingpin, sets token, paired_at, and clientserial, revokes the pairingpin, and returns device credentials and company metadata.\n", + "security": [], + "x-markdown": "pos/client.md", + "parameters": [ + { + "in": "query", + "name": "authCode", + "required": true, + "schema": { + "type": "string" + }, + "description": "Discovery code (1-4 digits) or pairing PIN (6 digits)", + "example": "678677" + }, + { + "in": "query", + "name": "name", + "required": false, + "schema": { + "type": "string" + }, + "description": "Client device name or serial identifier (default 'JTL-POS')", + "example": "Kasse 1" + } + ], + "responses": { + "200": { + "description": "Discovery or pairing successful", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "authCode": { + "type": "string", + "nullable": true, + "example": "678677" + }, + "authToken": { + "type": "string", + "nullable": true, + "example": "df40ad2067954646abb0499548a52241" + }, + "certificateFingerprint": { + "type": "string", + "example": "30DD111E8048B57E6A4FB75AD7E63F7E657F1262" + }, + "certificateSerialNumber": { + "type": "string", + "example": "1BB1BD7844451E3AC6" + }, + "mandantId": { + "type": "string", + "example": "1" + }, + "mandantName": { + "type": "string", + "nullable": true, + "example": "eB-Standard" + }, + "mandantDatabase": { + "type": "string", + "nullable": true, + "example": "eazybusiness" + }, + "serverFingerprint": { + "type": "string", + "nullable": true, + "example": "30-DD-11-1E-80-48-B5-7E-6A-4F-B7-5A-D7-E6-3F-7E-65-7F-12-62" + }, + "name": { + "type": "string", + "nullable": true + }, + "serverTimestamp": { + "type": "string", + "example": "2026-09-29 11:00:00" + } + } + } + } + } + }, + "400": { + "description": "Invalid or incorrect authentication code", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "Message": { + "type": "string", + "example": "Der Authentifizierungscode ist falsch." + } + } + } + } + } + } + } + } } }, "components": { diff --git a/docs/api/routes/pos/client.md b/docs/api/routes/pos/client.md new file mode 100644 index 0000000..666755b --- /dev/null +++ b/docs/api/routes/pos/client.md @@ -0,0 +1,46 @@ +# `GET /v1/client` + +Public. No cookie required. + +POS discovery and pairing + +Special API for POS bind and sync. +- Step 1 (Discovery): `authCode` length 1–4. Returns certificate fingerprints, serial number, and mandantId. +- Step 2 (Pairing): `authCode` length 6 (pairingpin from posterminal). Verifies the pairingpin, sets token, paired_at, and clientserial, revokes the pairingpin, and returns device credentials and company metadata. + + +## Query parameters + +| Param | Required | Type | Description | +|-------|----------|------|-------------| +| `authCode` | yes | string | Discovery code (1-4 digits) or pairing PIN (6 digits) | +| `name` | no | string | Client device name or serial identifier (default 'JTL-POS') | + +## Response `200` + +Discovery or pairing successful + +```json +{ + "authCode": "678677", + "authToken": "df40ad2067954646abb0499548a52241", + "certificateFingerprint": "30DD111E8048B57E6A4FB75AD7E63F7E657F1262", + "certificateSerialNumber": "1BB1BD7844451E3AC6", + "mandantId": "1", + "mandantName": "eB-Standard", + "mandantDatabase": "eazybusiness", + "serverFingerprint": "30-DD-11-1E-80-48-B5-7E-6A-4F-B7-5A-D7-E6-3F-7E-65-7F-12-62", + "name": "…", + "serverTimestamp": "2026-09-29 11:00:00" +} +``` + +## Errors + +| Status | Description | +|--------|-------------| +| 400 | Invalid or incorrect authentication code | + +--- + +_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._ diff --git a/scripts/generateApiDocs.js b/scripts/generateApiDocs.js index 6f0633c..37fbf53 100644 --- a/scripts/generateApiDocs.js +++ b/scripts/generateApiDocs.js @@ -13,7 +13,7 @@ const root = path.resolve(__dirname, '..'); const docsDir = path.join(root, 'docs', 'api'); const routesDocsDir = path.join(docsDir, 'routes'); -const PUBLIC_PATHS = new Set(['/auth/login', '/auth/logout', '/health']); +const PUBLIC_PATHS = new Set(['/auth/login', '/auth/logout', '/health', '/v1/client']); const options = { definition: { @@ -26,10 +26,14 @@ const options = { 'Browser clients must send cookies (`credentials: \'include\'`). ' + 'Sessions use an HTTP-only `token` cookie (`Secure`, `SameSite=Strict`, 24h); HTTPS is required for login.', }, - servers: [{ url: 'http://localhost:4208/api', description: 'Local default (PORT / .env)' }], + servers: [ + { url: 'http://localhost:4208/api', description: 'Local default HTTP (PORT / .env)' }, + { url: 'https://localhost:8443', description: 'Local default HTTPS (HTTPS_PORT / .env)' }, + ], tags: [ { name: 'Auth', description: 'Google SSO and session' }, { name: 'Health', description: 'Connectivity checks' }, + { name: 'POS', description: 'POS bind and sync API (/v1)' }, { name: 'Events', description: 'SSE update signals for live clients' }, { name: 'Article', description: 'Articles under categories' }, { name: 'Category', description: 'Category tree and membership' }, @@ -55,6 +59,7 @@ const options = { apis: [ path.join(root, 'src/api/openapi.js'), path.join(root, 'src/api/routes/**/*.js'), + path.join(root, 'src/v1/**/*.js'), ], }; @@ -352,6 +357,8 @@ function renderReadme(operations) { lines.push(''); lines.push('```'); lines.push('http://localhost:4208/api'); + lines.push('https://localhost:8443/api'); + lines.push('https://localhost:8443/v1 (POS API)'); lines.push('```'); lines.push(''); lines.push('## CORS & cookies'); diff --git a/src/v1/routes/client.js b/src/v1/routes/client.js index 266d660..d8ac038 100644 --- a/src/v1/routes/client.js +++ b/src/v1/routes/client.js @@ -56,7 +56,85 @@ export function buildClientStep2(authCode, config) { } /** - * Handles GET /v1/client + * @openapi + * /v1/client: + * get: + * tags: [POS] + * summary: POS discovery and pairing + * description: | + * Special API for POS bind and sync. + * - Step 1 (Discovery): `authCode` length 1–4. Returns certificate fingerprints, serial number, and mandantId. + * - Step 2 (Pairing): `authCode` length 6 (pairingpin from posterminal). Verifies the pairingpin, sets token, paired_at, and clientserial, revokes the pairingpin, and returns device credentials and company metadata. + * security: [] + * x-markdown: pos/client.md + * parameters: + * - in: query + * name: authCode + * required: true + * schema: + * type: string + * description: Discovery code (1-4 digits) or pairing PIN (6 digits) + * example: "678677" + * - in: query + * name: name + * required: false + * schema: + * type: string + * description: Client device name or serial identifier (default 'JTL-POS') + * example: "Kasse 1" + * responses: + * 200: + * description: Discovery or pairing successful + * content: + * application/json: + * schema: + * type: object + * properties: + * authCode: + * type: string + * nullable: true + * example: "678677" + * authToken: + * type: string + * nullable: true + * example: "df40ad2067954646abb0499548a52241" + * certificateFingerprint: + * type: string + * example: "30DD111E8048B57E6A4FB75AD7E63F7E657F1262" + * certificateSerialNumber: + * type: string + * example: "1BB1BD7844451E3AC6" + * mandantId: + * type: string + * example: "1" + * mandantName: + * type: string + * nullable: true + * example: "eB-Standard" + * mandantDatabase: + * type: string + * nullable: true + * example: "eazybusiness" + * serverFingerprint: + * type: string + * nullable: true + * example: "30-DD-11-1E-80-48-B5-7E-6A-4F-B7-5A-D7-E6-3F-7E-65-7F-12-62" + * name: + * type: string + * nullable: true + * serverTimestamp: + * type: string + * example: "2026-09-29 11:00:00" + * 400: + * description: Invalid or incorrect authentication code + * content: + * application/json: + * schema: + * type: object + * properties: + * Message: + * type: string + * example: "Der Authentifizierungscode ist falsch." * @param {import('express').Request} req * @param {import('express').Response} res */