AI-/Lovable-prompter
Kör prompterna i ordning. Tillsammans producerar de mönstret på [Golden path](/docs/golden-path) — inte inloggning vid varje öppning.
1. KiiOn Auth#
Inloggning och säker server-side-lagring av tokenparet.
1. KiiOn Auth
Build a KiiOn API client in <language/framework>.
Base URL: https://staging.zesec.com/api
Login: POST /auth/login-by-phone with body {"phoneNumber": "...", "password": "..."}
Response: {"AccessToken": "...", "RefreshToken": "..."}
Requirements:
- Credentials come from environment variables, never from code or the repository.
- The token pair is stored SERVER-SIDE in shared storage (database or cache), one record per
API account. It must never reach a browser, a mobile app or a log line.
- Expose exactly one entry point, getValidAccessToken(). No other code may call login().
- The account allows ONE active session, so the client must be a singleton per account.
Do not implement refresh yet. Just login, storage and the singleton boundary.2. Token-förnyelse#
Proaktiv förnyelse, 401-fallback och roterande engångs-refresh-token.
2. Token renewal
Extend the client with token renewal.
Refresh: POST /auth/refresh with body {"refreshToken": "<RefreshToken>"}
Response: {"AccessToken": "...", "RefreshToken": "..."} — BOTH values are new.
Requirements:
- The refresh token is single-use. The one you sent is dead the instant the response arrives.
Persist the new access token AND the new refresh token in ONE atomic write.
- Refresh proactively every 36 hours, or earlier if the JWT "exp" claim says so.
Do not wait for a 401.
- Guard renewal with a single-flight lock: concurrent callers must await one refresh, never
start two, or the second burns a token the first already consumed.
- On 400/401 from /auth/refresh the token is spent — fall back to a full login. Never retry the
same refresh token.
- If the login fallback also fails, stop and surface the error. Do not loop.
- Wrap API calls so a 401 triggers one refresh and one replay of the original request, then gives up.3. Enhetssynk + mappning#
Hämta enhetslistan och cachelagra mappningen mot era egna dörrar.
3. Device sync + mapping
Add device synchronisation.
Device list: GET /user/device with header "Authorization: Bearer <AccessToken>"
Response: array of {"id": <int>, "name": "...", "locationId": <int>, "locationName": "...", "online": <bool>}
Requirements:
- The "id" field is the {deviceId} used by the unlock endpoint. It is an integer.
- Run syncDevices() on a schedule (roughly daily) and after any 404 from an unlock, never on the
unlock path itself.
- Persist a mapping from OUR internal door identifier to the KiiOn id, keeping name, location and
a syncedAt timestamp for debugging.
- Never hard-code a device id anywhere in the codebase.4. Unlock-funktion#
Ett anrop i öppningsögonblicket, med typade fel och audit.
4. Unlock function
Add the unlock function.
Unlock: PUT /device/{deviceId}/unlock with header "Authorization: Bearer <AccessToken>"
A 200 means the command was accepted and forwarded to the device.
Requirements:
- The signature takes OUR internal door identifier, not a KiiOn id.
- Check our own authorization rules first. KiiOn does not decide who may open.
- Resolve {deviceId} from the cached mapping. Exactly one HTTP call happens here.
- Never retry an unlock automatically — a repeated call can open the door a second time. On
failure, return a typed error: 401 handled by the token layer, 403 = no key for this device,
404 = stale mapping (trigger a re-sync), 5xx = device or gateway unreachable.
- Write every attempt to our own audit log with actor, door, deviceId and outcome.5. UI med öppna-knapp per dörr#
Ett gränssnitt som aldrig ser en token.
5. UI with an Open button per door
Build a UI that lists doors and gives each one an Open button.
Requirements:
- The UI talks ONLY to our own backend. It never sees a KiiOn token, base URL or device id.
- List the doors the signed-in user is allowed to open, using our own authorization rules.
- The Open button calls our backend endpoint, disables itself while the request is in flight and
shows a clear result: opened, not permitted, door unreachable, or try again later.
- No automatic retry on failure — the user decides whether to press again.
- Show when a door is offline so the user is not left guessing.Vidare
Kopiera sidan som Markdown: Visa som Markdown