Skip to content

feat(webapi): expose the core's shared-directory configuration - #531

Merged
got3nks merged 1 commit into
amule-org:masterfrom
got3nks:feat/amuleapi-shared-dirs
Jul 20, 2026
Merged

feat(webapi): expose the core's shared-directory configuration#531
got3nks merged 1 commit into
amule-org:masterfrom
got3nks:feat/amuleapi-shared-dirs

Conversation

@got3nks

@got3nks got3nks commented Jul 20, 2026

Copy link
Copy Markdown

Follow-up to #530, which made the core's share roots reachable over EC. amuleapi could already list the files a share produced (/shared) and ask the core to re-walk its roots (/shared/reload), but had no way to see or change which roots those were.

Endpoints

Verb Purpose
GET /api/v0/shared/directories the configured roots as {path, recursive}GUEST
PUT /api/v0/shared/directories replace the whole set — ADMIN
POST /api/v0/shared/directories add one root, idempotent — ADMIN
DELETE /api/v0/shared/directories?path=… remove one root — ADMIN

GET returns the user's intent, not the runtime expansion: a recursive root is a single entry however many subdirectories it covers. The derived union is deliberately not exposed — it's generated state, not configuration. Read access is GUEST to match GET /shared and GET /preferences, both of which already expose paths.

PUT mirrors the core's operation one-for-one rather than hiding a read-modify-write behind a merge. POST/DELETE exist because the scripted case is a single folder, and making every client read-splice-write the whole list is how clients get it wrong. POST is idempotent (re-adding updates the recursive flag) so "ensure this is shared" repeats safely; DELETE 404s on an unconfigured path so a typo is visible.

Validation

The core validates each path — a REST client can't stat the core's filesystem, so a typo would otherwise become a silently dead share. Entries that pass are applied, persisted and rescanned; the rest come back in rejected, and one bad path never discards the edit:

{ "ok": true, "rejected": [ { "path": "/typo", "reason": "not_found" } ] }

Reasons arrive from the core as codes and are rendered here, so its locale can't leak into a response — the same approach amuleGUI uses.

Concurrency

POST and DELETE are read-modify-write against a whole-list operation, so they hold a mutex across the read and the write: SendRecvSerialized locks per round-trip, not across a pair, and two concurrent adds would otherwise lose one. Nothing can make this atomic against a simultaneous amuleGUI edit — the protocol has no compare-and-set — so that stays last-write-wins, and the reference documents it rather than implying otherwise.

Testing

New 31-shared-directories.sh curl smoke, registered in run-all.sh. It snapshots the operator's real configuration and restores it on exit (including early exit), and works entirely under a mktemp directory. Covers auth/method/body guards, an add → read-back → delete round trip, idempotent re-add, and a server-side rejection landing alongside a valid entry that survives.

30/30 passing against a live amuled + amuleapi. clang-format clean across src/, Tier-1 and Tier-2 clang-tidy clean. No .po impact — the reason strings are wire values, not translatable text.

amuleapi could list the files a share produced (/shared) and ask the core to
re-walk its roots (/shared/reload), but had no way to see or change which roots
those were — that configuration only became reachable over EC in amule-project#530.

Add /shared/directories with the four verbs a client actually needs:

- GET returns the roots as {path, recursive}, i.e. the user's intent rather
  than the runtime expansion. A recursive root is one entry however many
  subdirectories it covers; the derived union stays hidden because it is
  generated state, not configuration. GUEST, matching GET /shared and
  GET /preferences.
- PUT replaces the whole set, mirroring the core's operation one-for-one so the
  API doesn't hide a read-modify-write behind a merge.
- POST adds a single root and DELETE removes one, because the scripted case is
  a single folder and making every client read-splice-write the whole list is
  how clients get it wrong. POST is idempotent (re-adding updates the recursive
  flag) so "ensure this is shared" repeats safely; DELETE 404s on a path that
  isn't configured so a typo is visible.

The core validates each path, since a REST client cannot stat the core's
filesystem: entries that pass are applied and rescanned, the rest come back in
`rejected` with a reason, and one bad path never discards the edit. Reasons
arrive as codes and are rendered here, so the core's locale cannot leak into a
response.

POST and DELETE are read-modify-write against a whole-list operation, so they
take a mutex across the read and the write — SendRecvSerialized locks per
roundtrip, not across a pair, and two concurrent adds would otherwise lose one.
Nothing can make this atomic against a simultaneous amuleGUI edit (the protocol
has no compare-and-set); that stays last-write-wins and is documented as such.

Covered by a new curl smoke that snapshots the operator's real configuration and
restores it on exit, exercising the auth/method/body guards, an add/read-back/
delete round trip, idempotent re-add, and a server-side rejection alongside a
valid entry.
@got3nks
got3nks merged commit ad5fb2f into amule-org:master Jul 20, 2026
14 checks passed
@got3nks

got3nks commented Jul 20, 2026

Copy link
Copy Markdown
Author

@ngosang heads-up — this adds shared-directory management to the API, which would fit nicely in the frontend whenever you get to it. No rush at all, entirely at your own pace.

Four verbs on /api/v0/shared/directories:

  • GET — the configured roots as {path, recursive} (GUEST)
  • PUT — replace the whole set (ADMIN)
  • POST — add one root; idempotent, so re-adding just updates the recursive flag (ADMIN)
  • DELETE ?path=… — remove one (ADMIN)

The write verbs return {ok, rejected}. rejected lists any path the core refused, with a reason (not_found / not_readable) — and the valid entries still apply, so the UI can flag exactly which ones failed rather than treating the whole edit as lost. Worth knowing for the frontend: the paths are on the core's filesystem, so the browser can't validate them; that rejection list is the only way to tell the user a path was wrong.

GET returns the user's intent rather than the expansion — a recursive root is one entry however many subdirectories it covers.

Full details in docs/api/REFERENCE.md. The matching amuleGUI editor landed in #530, so both front-ends would end up offering the same capability.

@got3nks
got3nks deleted the feat/amuleapi-shared-dirs branch July 20, 2026 13:31
got3nks added a commit that referenced this pull request Jul 20, 2026
…th encoding (#534)

Follow-up to #531. The endpoint index listed only GET and PUT /shared/directories; the POST and DELETE sections existed in the body but were unreachable from the summary, so add the two links.

Also document what the DELETE query parameter requires: the exact path GET returned, percent-encoded, matched byte-for-byte by the daemon. Add a Windows example alongside the POSIX one so the backslash / drive-letter-colon / space encoding is shown. No code change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant