This page explains what happens between the first byte of index.html and a usable home screen, and how every later request reaches the API. It covers main.js, api.js, app-session.js, gen-api.js, meta.js, and data-bus.js in webapp/js/. Token handling on the server is described at mechanism level on authentication and sessions.
In short¶
boot()applies stored preferences, loads the language, wires the sign-in form, and then either proves the session withGET /auth/meor shows the sign-in screen (webapp/js/main.js:211-252).- The session lives in HttpOnly cookies. Script code keeps only the signed-in user and the permission list as a display hint (
webapp/js/api.js:1-6,webapp/js/api.js:26-28). - Before the home screen appears, the app catalogue, the metadata document, and the user UI state are requested in parallel (
webapp/js/home.js:439-474). - All metadata-driven calls go through
GenApi, which builds every path from metadata endpoint declarations (webapp/js/gen-api.js:37-61). document.documentElement.dataset.appReadyis set to1when boot ends, also after a failure (webapp/js/main.js:274-280).
How it works¶
Boot sequence¶
The sequence diagram shows the path from page load to the home screen for a returning user.
sequenceDiagram participant B as Browser participant S as Server participant M as main.js participant H as home.js B->>S: GET /app/ S-->>B: index.html with stamp filled in B->>M: import main.js M->>M: initPrefs, initA11y, initI18n M->>S: GET /api/v1/auth/me S-->>M: user and permissions M->>H: loadAppCatalog H->>S: GET /_meta, /_switches, /_overlay H->>S: GET /auth/ui-state H->>S: GET /app_store/registry H->>H: initHome, hydrateFromServer, restoreSession M->>B: data-app-ready = 1
In words:
- The server returns
index.htmlwith the asset stamp filled in (src/cluster_erp/server/app.py:1616-1625at commit 92f8051). The page loadsjs/main.jsas a module (webapp/index.html:406). boot()callsinitPrefs()for theme and density andinitA11y()for accessibility settings, both before first paint (webapp/js/main.js:212-213).initI18n()readsi18n/languages.json, picks a language, and loads its dictionary.apply()translates the static markup (webapp/js/main.js:218-221). See internationalisation and languages.- Boot installs the offline banner, the sign-in form, the signup entry, the logout handler, the home duplication button, the custom select upgrade, and the table header alignment (
webapp/js/main.js:222-231). - If a stored user exists,
fetchMe()callsGET /auth/me. Success shows the home screen; failure clears the local session and shows the sign-in form (webapp/js/main.js:233-251). showHome()awaitsloadAppCatalog()before it reveals the home screen, so a half-initialised screen never flashes (webapp/js/main.js:31-41).loadAppCatalog()startsloadMeta()andloadUiState(), then requestsGET /app_store/registry. It retries once and showshome.catalog_errorif both attempts fail (webapp/js/home.js:439-474).initHome()applies the board profile for the device class, reconciles the saved layout, mounts the dock, the floating button, and the status line, adopts the server copy of the layout, and reopens the apps that were open before a reload (webapp/js/home.js:4479-4562).
Session and sign-in¶
login() posts the user name, the password, and the remember-me flag to POST /auth/login and asks for the cookie transport. The server answers with HttpOnly cookies and the user payload. The client stores the user and the permission list in local storage (webapp/js/api.js:185-205). On a multi-tenant deployment, the domain part of a company e-mail address selects the tenant (webapp/js/api.js:179-184). Tenant routing is described on multi-tenancy.
isAuthenticated() only checks that a stored user exists. The comment states that this hint is the only thing script code can see of the session (webapp/js/api.js:24-28). The server proves the session on every call.
hasPermission(code) answers from the stored permission list. When no list is stored it answers true. The comment states that client-side gating is a display aid and the server enforces every permission (webapp/js/api.js:49-55). See roles and permissions.
logout() clears the local hint, clears every unsaved form draft, and posts POST /auth/logout so the server revokes the session (webapp/js/api.js:207-226). The cluster:logout handler in main.js also collapses duplicated homes, resets the assistant and mail state, and reloads the page so that the next sign-in runs the newest deployed code (webapp/js/main.js:163-177).
API calls¶
apiFetch(path, options) prefixes /api/v1, sends JSON, adds a custom request header used by the server as a cross-site request check, and adds Accept-Language with the current language (webapp/js/api.js:10-22, webapp/js/api.js:129-144). A non-2xx answer raises ApiError with the status and the parsed body (webapp/js/api.js:86-92). A network failure raises the browser's TypeError.
On a 401 answer for a signed-in user, the client tries one session refresh and repeats the request once. Parallel 401 answers share a single refresh request (webapp/js/api.js:94-113, webapp/js/api.js:138-141). apiFetchBlob and apiUpload follow the same rule for files and multipart uploads (webapp/js/api.js:151-177).
apiErrorText(ex) turns an error into a message. It prefers the translation of error.<code> from the language file, filled with the server's error details, and falls back to the server's message (webapp/js/gen-api.js:11-35). Error codes are listed on errors and exceptions.
The GenApi class¶
GenApi(moduleName, entityMeta) builds paths as /<module><endpoint path> with {id}, {parent_id}, and {scope_id} replaced (webapp/js/gen-api.js:37-61).
| Method | HTTP call | Source |
|---|---|---|
list |
GET of the list endpoint with search, limit, offset (only when the list view is paginated), parent and scope filters, and extra filters |
webapp/js/gen-api.js:65-92 |
get |
GET of the get endpoint |
webapp/js/gen-api.js:94-96 |
create |
POST of the create endpoint |
webapp/js/gen-api.js:98-105 |
update |
The method declared on the update endpoint, PUT when none is declared |
webapp/js/gen-api.js:107-117 |
remove |
DELETE of the delete endpoint, with optional deactivation headers |
webapp/js/gen-api.js:121-128 |
bulkDelete |
POST of the bulk_delete endpoint with {ids} |
webapp/js/gen-api.js:130-138 |
reactivate |
POST /records/<module>/<entity_type>/<id>/activate |
webapp/js/gen-api.js:141-148 |
listInactive |
GET /records/<module>/<entity_type>/inactive |
webapp/js/gen-api.js:151-159 |
action |
The method and path of an ActionMeta endpoint; GET sends no change signal |
webapp/js/gen-api.js:164-174 |
Every mutating method calls _changed, which drops the module's cached reference lists and emits the data-changed signal (webapp/js/gen-api.js:48-51). See data refresh and change signals. Deletion is a deactivation on the server; see soft delete and deactivation.
Reference list cache¶
fkItems, fkPreload, and fkItem load whole option lists for foreign-key pickers and columns and cache them per module, endpoint, and parameters (webapp/js/gen-api.js:189-298). A list is paged through in full. The remaining pages are requested six at a time, with the page size the server echoed and a ceiling of 200 per request (webapp/js/gen-api.js:194, webapp/js/gen-api.js:203-251). resolveMissingLabels asks GET /records/<module>/<entity_type>/labels for ids that are not in a list, so that deactivated records still show a title (webapp/js/gen-api.js:320-351). The cache is cleared on logout and on a language change (webapp/js/gen-api.js:362-371).
Metadata load¶
loadMeta() requests /_meta, /_switches, and /_overlay in parallel and shares one request among concurrent callers (webapp/js/meta.js:16-47). A failure of /_switches treats every switch as on; a failure of /_overlay keeps the shipped form (webapp/js/meta.js:21-25). The document is then pruned and overlaid; see metadata rendering.
Reload and open apps¶
app-session.js writes only the ids of open apps and the front app to session storage under cluster.session.open_apps (webapp/js/app-session.js:17-30). After a reload, restoreSession() reopens those apps at their root screen, skipping apps that are no longer installed (webapp/js/home.js:2583-2598). Logout clears the entry (webapp/js/app-session.js:52-53).
Why it is built this way¶
- Cookie session: the comment in
webapp/js/api.js:1-6states that script code never holds a token, so nothing in this origin can read one. - Readiness attribute: the comment in
webapp/js/main.js:268-273states that the sign-in button exists before boot finishes, so the attribute is the signal that controls are wired. - Reload after logout: the comment in
webapp/js/main.js:170-174states that a full reload makes the next sign-in run the newest deployed code. - Six parallel pages: the comment in
webapp/js/gen-api.js:219-222states that a sequential walk made large pickers wait for seconds.
Rules and boundaries¶
- A view never builds an entity path by hand when metadata declares it.
GenApi.pathraises an error for an undeclared endpoint key (webapp/js/gen-api.js:53-56). - Cached option lists never outlive the session (
webapp/js/gen-api.js:360-365). - Browser tests pin the session and reload behaviour:
web-client/_e2e_cookie_session.mjs,web-client/_e2e_session_remember.mjs, andweb-client/_e2e_refresh_session.mjs.
Interaction with other parts¶
GET /api/v1/_metaandGET /api/v1/_switchesare served bysrc/cluster_erp/server/app.py:1518-1572(at commit 92f8051).GET /api/v1/_overlayis served bysrc/cluster_erp/server/routes/tenant_fields.py:446.GET /api/v1/app_store/registryis served bysrc/cluster_erp/modules/app_store/routes.py:18-40. See app store module.- The records endpoints used for labels, inactive lists, and reactivation belong to the
recordsmodule; see records module.
Limits and open points¶
restoreSession()cannot restore pushed sub-screens, because the view stack holds render functions. The comment calls this a deliberate limit (webapp/js/home.js:2579-2582).- A failed gated-app or metadata request is retried only where the code says so.
loadMeta()returnsfalseon failure and later callers request it again (webapp/js/meta.js:42-46).
Terms introduced¶
| Term | Code identifier | Definition | Turkish term | Source |
|---|---|---|---|---|
| generic API | GenApi |
The client class that builds entity calls from metadata endpoint declarations. | genel API | webapp/js/gen-api.js:37 |
| reference list cache | listCache |
The in-memory cache of whole foreign-key option lists, keyed by module, endpoint, and parameters. | referans liste önbelleği | webapp/js/gen-api.js:189 |
| readiness attribute | data-app-ready |
The attribute on the root element that marks the end of boot. | hazır olma niteliği | webapp/js/main.js:279 |
Related pages¶
Verified against commit 92f8051 on 2026-11-13.