# API Specification (code-backed): races, images, and authentication ## Scope and evidence This document is reverse-engineered from: - JSP/JS usage in the web app - `WEB-INF/web.xml` servlet mappings - decompiled sources in `WEB-INF/lib/*_src` (notably `AblServletSvlt`, `AcServlet`, `GaraSvlt`, `Logon4Svlt`, `Menu4Svlt`, `GetFileTnSvlt`) The upload response schemas below are now **confirmed from code**, not inferred. ## Base URL Examples use: - `https://your-host` ## Authentication (how to obtain the usable token) ## Actual auth model This app uses **server session authentication**, not JWT bearer tokens. Login success sets session attributes: - `loginUser_id` (Long) - `utenteLogon` (user object) For automation, the practical token is the **session cookie** (`JSESSIONID` and any app cookies). ## Admin login endpoint - `POST /admin/menu/Menu4.abl` - Servlet mapping: `com.ablia.anag.servlet.Menu4Svlt` (extends `Logon4Svlt`) - Body (`application/x-www-form-urlencoded`): - `login` - `pwd` - `cmdIU=check` Other `cmdIU` values used: - `cmdIU=login` (logout) - `cmdIU=np` (password change) - `cmdIU=checkSso` (SSO branch if enabled) ### cURL login example (capture session cookie) ```bash curl -i -c cookies.txt -X POST "https://your-host/admin/menu/Menu4.abl" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data "login=YOUR_USER&pwd=YOUR_PASSWORD&cmdIU=check" ``` Reuse cookie jar for authenticated calls: ```bash curl -i -b cookies.txt "https://your-host/admin/pg/Gara.abl?cmd=search" ``` ## Token note (Bearer/Basic) - No `Authorization: Bearer ...` flow is present in inspected sources. - A helper exists to parse `Authorization: Basic` (`getBasicAuthorizationHeaders`), but no race/photo endpoint uses it directly. ## Relevant endpoints ## Admin race/photo endpoints - `POST|GET /admin/pg/Gara.abl` - `POST|GET /admin/pg/Foto.abl` - `POST|GET /admin/pg/TipoGara.abl` - `POST|GET /admin/pg/LogFoto.abl` Deployment caveat: - some deployments expose the same admin pages under `POST|GET /admin/pg_RUS/*.abl`. - On `https://www.regalamiunsorriso.it`, post-login dashboard links point to `/admin/pg_RUS/Gara.abl` (not `/admin/pg/Gara.abl`). ## Public/web endpoints related to photos/users - `POST|GET /Foto2.abl` - `POST|GET /Logon.abl` (mapped to `com.ablia.pg.servlet.Logon2Svlt`) - `POST|GET /Users.abl` Note: `/Login.abl` in `web.xml` is mapped to cart servlet (`CartSvlt`), not admin login. ## File serving endpoints - `GET /foto/*` → `GetFileTnSvlt` (thumbnail by default) - `GET /fotoOriginali/*` → `GetFileOrigSvlt` (original file flow) ## Multipart upload contract (common engine) All race-image/file uploads go through `AblServletSvlt.manageImgFileMultipartRequest` + `AcServlet.manageMultipartRequestParameters`. Key behavior: - File fields explicitly recognized: `imgFile`, `nomeFile` - UI also sends `fileName` for generic file upload; this still works (stored by original filename) - Temporary target directory: `DOCBASE + tmp/` (`getPathTmp()`) - Per-upload file size target: about `20000 KB` for this servlet path ## Upload race image Endpoint: - `POST /admin/pg/Gara.abl` Required form fields: - `cmd=loadImg` - `imgFile` (binary) - `id` (race id) - `codImage` (slot index) - `totImgNumber` (typically `3` in UI) Storage details: - Race attachment path from `Gara.getPathAttach()` is `_img/_gara/` - Thumbnail is generated into `_img/_gara/100/` (100x75) ### Response schema (confirmed) Response is JSON array with one object (`JsonUploadImageResponse`): - `result` (boolean) - `message` (string) - `imgPath` (string) Example success payload shape: ```json [ { "result": true, "message": "...Immagine 1 Salvata...", "imgPath": "../../_img/_gara/100/" } ] ``` ## Delete race image Endpoint: - `POST /admin/pg/Gara.abl` Fields: - `cmd=removeImg` - `id` - `codImage` - `totImgNumber` Response schema is the same `JsonUploadImageResponse[]`. ## Upload CSV/file for race processing Endpoint: - `POST /admin/pg/Gara.abl` UI helper (`Ab.saveFile`) sends: - `cmd=saveFile` - `fileName` (binary file) - `codFile` (slot id, usually `1`) - `id` (often `0` in UI flow) ### Response schema (confirmed) JSON array with one `JsonUploadFileResponse` object: - `result` (boolean) - `message` (string) - `fileName` (stored/original name) - `fileNameLink` (typically `../../tmp/`) Example shape: ```json [ { "result": true, "message": "...", "fileName": "punti-foto.csv", "fileNameLink": "../../tmp/punti-foto.csv" } ] ``` ## Race command API (`/admin/pg/Gara.abl`) Confirmed custom commands in `GaraSvlt`: - `asq` + `act=save` (generic save used by admin UI toolbar) - `ni` (new record flow before save) - `addPuntoFoto` - `delPuntoFoto` - `modPuntoFoto` - `indexFoto` - `noIndexFoto` - `creaPuntiFoto` - `indexCsvPisa` - `salvaFileCsv` Important parameters by command: - `id_gara` for race-level operations - `id_puntoFoto` and `id_puntoFotoIdx` for point selection - point fields such as `descrizionePuntoFoto`, `pathRelativoFoto`, `tipoPuntoFoto` Race save payload fields (from `admin/pg_RUS/gara.jsp` + `_V4/_js/_bean.js`): - mandatory in practice: `descrizione`, `dataGaraInizio`, `id_tipoGara` - commonly sent: `dataGaraFine`, `flgEventoInLinea`, `flgTipoIndex`, `pathBase`, `flgFree`, `localita` - command envelope for save: `cmd=asq`, `act=save` Confirmed fixed-flag value sets from UI templates: - `flgEventoInLinea`: `0` (Non In Linea), `1` (Stand By), `2` (In Linea) - `flgTipoIndex`: `0`, `1` - `flgFree`: `0` (No), `1` (SI) Server-side normalization behavior: - `Gara.save()` appends trailing `/` to `pathBase` if missing. - if `pathBase` is empty and `dataGaraInizio` is set, server auto-derives `pathBase` as `//`. - `PuntoFoto.prepareSave()` appends trailing `/` to `pathRelativoFoto` if missing. CSV flow coupling: 1. `cmd=saveFile` uploads to `tmp/` and returns `fileName` 2. UI stores it into hidden field `fileNameOnServer_1` 3. `cmd=salvaFileCsv` copies `DOCBASE/tmp/` to `DOCBASE/admin/csv/.csv` 4. `cmd=indexCsvPisa` reads `admin/csv/.csv` via `Gara.getImpCsvFileName()` and updates matching photos ## Processed photo transfer channel (`/ReceiveFile.abl`) The original 3-piano -> WWW photo push does not use `Gara.abl` multipart for each processed photo. It uses `UploadFile` against a dedicated receiver servlet: - `POST /ReceiveFile.abl` - servlet class: `com.ablia.servlet.ReceiveFileSvlt` - typically unauthenticated (`isSecureServlet=false` in decompiled source) Observed request shape: - query params: `name`, `path`, `overwriteRemoteFile`, `bs` - request body: raw file bytes stream Usage in workflow: - destination path is computed per punto-foto on remote side (`puntoFotoR.getPathCompletoFoto()`) - processed image and thumbnail (`tn_`) are both transferred - after transfer, photo flags are updated/indexed via DB-backed commands (`indexFoto` / CSV/indexing flows) ## Image retrieval behavior `/foto/*` (`GetFileTnSvlt`): - accepts `id_foto` query parameter, or extracts id from URL suffix pattern like `name-.jpg` - if photo not found, serves `_img/_imgNotFound.png` - returns thumbnail by default in this servlet flow `/fotoOriginali/*` (`GetFileOrigSvlt`): - routes to original-file logic - applies user/account checks (valid user, not expired, max photos) - blocks some originals by filename markers (`_X`, `_Y`, `_Z`) depending on profile - logs photo view events when original is served ## Known response caveat Do not treat `result=true` as universally reliable for success semantics: - in some error branches (`_loadImg`, `_removeImg`, `_saveFile`), code still returns `result=true` with an error message. For robust clients, validate both: - `result` - `message` text + expected output field (`imgPath` / `fileName` non-empty) ## Practical automation sequence 1. Login on `/admin/menu/Menu4.abl` with `cmdIU=check`, store cookies. 2. Create race on `/admin/pg_RUS/Gara.abl` with `cmd=asq`, `act=save` and race fields. 3. Create and/or manage punti foto (`addPuntoFoto`, `creaPuntiFoto`, `modPuntoFoto`). 4. Transfer processed files with `/ReceiveFile.abl` to remote race/punto paths. 5. Trigger indexing (`indexFoto` per punto, or CSV via `saveFile` + `salvaFileCsv` + `indexCsvPisa`). 6. Read thumbnails from `/foto/*` and originals from `/fotoOriginali/*`.