diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 69f1e5b..ecaf253 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -32,6 +32,8 @@ Matrix clients. (#35) - Moved all administrator API endpoints to `/_telodendria/admin/v1`, because later revisions of the administrator API may break clients, so we want a way to give those breaking revisions new endpoints. +- Added a **PUT** option to `/_telodendria/admin/v1/config` that gives the ability to change +only a subset of the configuration. - Implemented `/_telodendria/admin/v1/deactivate/[localpart]` for admins to be able to deactivate users. - Implemented the following APIs for managing registration tokens: diff --git a/docs/user/admin/config.md b/docs/user/admin/config.md index 0466403..8394f7b 100644 --- a/docs/user/admin/config.md +++ b/docs/user/admin/config.md @@ -4,7 +4,7 @@ As mentioned in [Setup](../setup.md), Telodendria's configuration is intended to be managed via the configuration API. Consult the [Configuration](../config.md) document for a complete list of supported configuration options. This document simply describes the API used to -update the configuration. +update the configuration described in that document. ## API Endpoints @@ -40,3 +40,23 @@ configuration with the new one. |-------|------|-------------| | `restart_required` | `Boolean` | Whether or not the process needs to be restarted to finish applying the configuration. If this is `true`, then the restart endpoint should be used at a convenient time to apply the configuration. +### **PUT** `/_telodendria/admin/config` + +Update the currently installed configuration instead of completely replacing it. This endpoint +validates the request body, merges it on top of the current configuration, validates the resulting +configuration, then updates it in the database. This is useful when only one or two properties +in the configuration needs to be changed. + +| Requires Token | Rate Limited | +|----------------|--------------| +| Yes | Yes | + +| Response Code | Description | +|---------------|-------------| +| 200 | The new configuration was successfully installed.| + +#### 200 Response Format + +| Field | Type | Description | +|-------|------|-------------| +| `restart_required` | `Boolean` | Whether or not the process needs to be restarted to finish applying the configuration. If this is `true`, then the restart endpoint should be used at a convenient time to apply the configuration. diff --git a/src/Main.c b/src/Main.c index ecba436..4b25c9e 100644 --- a/src/Main.c +++ b/src/Main.c @@ -128,10 +128,6 @@ start: httpServers = NULL; restart = 0; - /* For getopt() */ - opterr = 1; - optind = 1; - /* Local variables */ exit = EXIT_SUCCESS; flags = 0; diff --git a/src/Routes/RouteConfig.c b/src/Routes/RouteConfig.c index 26a471e..5f0c91e 100644 --- a/src/Routes/RouteConfig.c +++ b/src/Routes/RouteConfig.c @@ -39,6 +39,7 @@ ROUTE_IMPL(RouteConfig, path, argp) HashMap *request = NULL; Config *newConf; + HashMap *newJson = NULL; (void) path; @@ -121,10 +122,56 @@ ROUTE_IMPL(RouteConfig, path, argp) JsonFree(request); break; case HTTP_PUT: - /* TODO: Support incremental changes to the config */ + request = JsonDecode(HttpServerStream(args->context)); + if (!request) + { + HttpResponseStatus(args->context, HTTP_BAD_REQUEST); + response = MatrixErrorCreate(M_NOT_JSON, NULL); + break; + } + + newJson = JsonDuplicate(DbJson(config->ref)); + JsonMerge(newJson, request); + + newConf = ConfigParse(newJson); + + if (!newConf) + { + HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); + response = MatrixErrorCreate(M_UNKNOWN, NULL); + break; + } + + if (newConf->ok) + { + if (DbJsonSet(config->ref, newJson)) + { + response = HashMapCreate(); + /* + * TODO: Apply configuration and set this only if a main + * component was reconfigured, such as the listeners. + */ + HashMapSet(response, "restart_required", JsonValueBoolean(1)); + } + else + { + HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); + response = MatrixErrorCreate(M_UNKNOWN, NULL); + } + } + else + { + HttpResponseStatus(args->context, HTTP_BAD_REQUEST); + response = MatrixErrorCreate(M_BAD_JSON, newConf->err); + } + + ConfigFree(newConf); + JsonFree(request); + JsonFree(newJson); + break; default: HttpResponseStatus(args->context, HTTP_BAD_REQUEST); - response = MatrixErrorCreate(M_UNRECOGNIZED, NULL); + response = MatrixErrorCreate(M_UNRECOGNIZED, "Unknown request method."); break; }