Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ You can define any custom container startup logic here if needed.
Heartbeat
*********

The ``GET /heartbeat`` method is called periodically by Nextcloud to check the ExApp health status,
i.e. if its webserver is running and receiving requests.
AppAPI calls ``GET /heartbeat`` periodically when it installs, updates or enables the ExApp, until it answers,
to check that its web server is up and running.

URL: ``GET http://localhost:2345/heartbeat``

AppAPI expects a response with HTTP status 200.
AppAPI expects HTTP status 200 with ``{"status": "ok"}``.
This step fails if the ExApp does not respond within 10 minutes.

.. note::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Options
* ``--json-info JSON-INFO`` *[optional]* - ExApp deploy JSON info (json string)

.. warning::
After successful deployment (pull, create, and start container), there is a heartbeat check with a 90 second timeout (will be configurable).
After successful deployment (pull, create, and start container), there is a heartbeat check with a 10 minute timeout.

Manual install for development
******************************
Expand Down Expand Up @@ -125,7 +125,7 @@ Application installation scheme
-------------------------------

1. AppAPI deploys the application and launches it.
2. For `N` seconds (default ``90``), AppAPI checks the ``/heartbeat`` endpoint with a ``GET`` request.
2. For up to 10 minutes, AppAPI checks the ``/heartbeat`` endpoint with a ``GET`` request.
3. AppAPI sends a ``POST`` request to the ``/init`` endpoint. If the ExApp does not implement the ``/init`` endpoint and AppAPI receives a 501 or 404 status code, AppAPI enables the application and goes straight to step 5.
4. The ExApp sends an integer from ``0`` to ``100`` to the OCS endpoint ``apps/app_api/apps/status`` indicating the initialization progress. After sending ``100``, the application is considered initialized.
5. AppAPI sends a ``PUT`` request to the ``/enabled`` endpoint.
Expand Down
33 changes: 29 additions & 4 deletions developer_manual/exapp_development/tech_details/api/routes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Routes
======

All ExApps must declare the routes that are allowed to be accessed via the AppAPI ExApp proxy (``/apps/app_api/proxy/*``).
All ExApps must declare the routes that users and clients may access, through the AppAPI ExApp proxy (``/apps/app_api/proxy/*``) or, with HaRP, directly (``/exapps/<appid>/*``).


Register
Expand Down Expand Up @@ -34,13 +34,38 @@ Example

where the fields are:

- ``url``: the route to be registered on the ExApp side, can be a regex
- ``verb``: the HTTP verb that the route will accept, can be a comma separated list of verbs
- ``url``: the route to be registered on the ExApp side, a regex matched from the start of the decoded request path, which begins with ``/`` (e.g. ``^/api/.*``); only HaRP matches it case-sensitively
- ``verb``: the HTTP verbs that the route accepts, a comma separated list (e.g. ``GET,POST``)
- ``access_level``: the name of the access level required to access the route, PUBLIC - public access without auth, USER - Nextcloud user auth required, ADMIN - admin user required
- ``headers_to_exclude``: a JSON-encoded string of an array of strings, the headers that the ExApp wants to be excluded from the request to it
- ``headers_to_exclude``: a JSON-encoded string of an array of strings, the headers that the ExApp wants to be excluded from the request to it; only the ExApp proxy applies it
- ``bruteforce_protection``: a JSON-encoded string of an array of numbers, the HTTP status codes that must trigger the bruteforce protection


Matching
^^^^^^^^

The first route whose ``url`` matches the path and whose ``verb`` lists the method decides the access level; ``HEAD`` is accepted wherever ``GET`` is.
If routes match the path but none lists the method, the request gets ``405``; the ExApp proxy serves only ``GET``, ``HEAD``, ``POST``, ``PUT`` and ``DELETE`` and answers ``404`` instead.
HaRP and the ExApp proxy also check the path as ExApp web servers may read it (in any letter case, without a trailing slash, without ``;`` parameters, with ``%2F`` kept in its segment), and the route of such a reading can only raise the access level.
Paths with ``.`` or ``..`` segments and the ``/heartbeat``, ``/init`` and ``/enabled`` endpoints are refused.

For example, with these routes in this order:

================ ============ ================
``url`` ``verb`` ``access_level``
================ ============ ================
``^/img(/.*)?$`` ``GET`` ``PUBLIC``
``^/api/.*`` ``GET,POST`` ``USER``
``.*`` ``GET`` ``ADMIN``
================ ============ ================

- ``GET /img/logo.png`` and ``HEAD /img/logo.png`` are public.
- ``GET /api/items`` needs a user: the ``USER`` route comes before ``.*``.
- ``DELETE /api/items`` gets ``405`` from HaRP and ``404`` from the ExApp proxy; ``PATCH /api/items`` gets ``405`` from both.
- ``GET /IMG/logo.png`` needs an admin: in this letter case only ``.*`` matches it.
- With ``^/img/.*`` as the first route, ``GET /img/`` would need an admin: without the trailing slash only ``.*`` matches it, so write a route for a directory as ``^/img(/.*)?$``.

@kyteinsky kyteinsky Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- With ``^/img/.*`` as the first route, ``GET /img/`` would need an admin: without the trailing slash only ``.*`` matches it, so write a route for a directory as ``^/img(/.*)?$``.
- With ``^/img/.*`` as the first route, ``GET /img/`` would need an admin since the check will be performed against ``GET /img/`` and ``GET /img`` (without the trailing slash). Which is matched by ``.*`` only, so write a route for a directory as ``^/img(/.*)?$``.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

had understood a bit wrong before, edited.
does this sound good?



Unregister
^^^^^^^^^^

Expand Down
Loading