Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Web app

Explanation

Boot, session, and API calls

How the web app starts in main.js, proves the session, loads the app catalogue and metadata, and calls the API through api.js and gen-api.js.

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 with GET /auth/me or 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.appReady is set to 1 when 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:

  1. The server returns index.html with the asset stamp filled in (src/cluster_erp/server/app.py:1616-1625 at commit 92f8051). The page loads js/main.js as a module (webapp/index.html:406).
  2. boot() calls initPrefs() for theme and density and initA11y() for accessibility settings, both before first paint (webapp/js/main.js:212-213).
  3. initI18n() reads i18n/languages.json, picks a language, and loads its dictionary. apply() translates the static markup (webapp/js/main.js:218-221). See internationalisation and languages.
  4. 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).
  5. If a stored user exists, fetchMe() calls GET /auth/me. Success shows the home screen; failure clears the local session and shows the sign-in form (webapp/js/main.js:233-251).
  6. showHome() awaits loadAppCatalog() before it reveals the home screen, so a half-initialised screen never flashes (webapp/js/main.js:31-41).
  7. loadAppCatalog() starts loadMeta() and loadUiState(), then requests GET /app_store/registry. It retries once and shows home.catalog_error if both attempts fail (webapp/js/home.js:439-474).
  8. 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-6 states 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-273 states 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-174 states 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-222 states 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.path raises 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, and web-client/_e2e_refresh_session.mjs.

Interaction with other parts

  • GET /api/v1/_meta and GET /api/v1/_switches are served by src/cluster_erp/server/app.py:1518-1572 (at commit 92f8051). GET /api/v1/_overlay is served by src/cluster_erp/server/routes/tenant_fields.py:446.
  • GET /api/v1/app_store/registry is served by src/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 records module; 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() returns false on 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

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13