feat(webapi): expose the core's shared-directory configuration - #531
Conversation
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.
|
@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
The write verbs return
Full details in |
…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.
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
GET /api/v0/shared/directories{path, recursive}— GUESTPUT /api/v0/shared/directoriesPOST /api/v0/shared/directoriesDELETE /api/v0/shared/directories?path=…GETreturns 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 isGUESTto matchGET /sharedandGET /preferences, both of which already expose paths.PUTmirrors the core's operation one-for-one rather than hiding a read-modify-write behind a merge.POST/DELETEexist because the scripted case is a single folder, and making every client read-splice-write the whole list is how clients get it wrong.POSTis idempotent (re-adding updates therecursiveflag) so "ensure this is shared" repeats safely;DELETE404s 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
POSTandDELETEare read-modify-write against a whole-list operation, so they hold a mutex across the read and the write:SendRecvSerializedlocks 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.shcurl smoke, registered inrun-all.sh. It snapshots the operator's real configuration and restores it on exit (including early exit), and works entirely under amktempdirectory. 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.poimpact — the reason strings are wire values, not translatable text.