diff --git a/docs/API.md b/docs/API.md index d4d85878..bfc9bc47 100644 --- a/docs/API.md +++ b/docs/API.md @@ -55,6 +55,17 @@ The response is a JSON object with fields: * `success`: whether the credentials provided were valid * `accountName`: canonical, case-unfolded version of the account name +`/v1/defcon` +------------ + +This endpoint can be used to view or modify the DEFCON level (see `/helpop defcon` for details). If the request is empty, the existing level is returned. To change the level, send a JSON object with fields: + +* `defcon`: integer, desired new value of the DEFCON setting (between 5 for normal operation and 1 for the most restrictive) + +The response is a JSON object with fields: + +* `defcon`: integer, current (or new) value of the DEFCON setting + `/v1/list` ---------- @@ -71,7 +82,7 @@ Each channel object has fields: * `hasKey`: boolean, whether the channel has a key set with the `+k` mode * `inviteOnly`: boolean, whether the channel has the `+i` invite-only mode set * `secret`: boolean, whether the channel has the `+s` secret mode set (and would be hidden from an unprivileged `LIST` command) -* `userCount`: int, number of users in the channel +* `userCount`: integer, number of users in the channel * `topic`: string, channel topic * `topicSetAt`: string, time the topic was last updated (in ISO8601 format) * `createdAt`: string, time the channel was created (in ISO8601 format) diff --git a/irc/api.go b/irc/api.go index baf489cc..2e00c054 100644 --- a/irc/api.go +++ b/irc/api.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/ergochat/ergo/irc/modes" + "github.com/ergochat/ergo/irc/sno" "github.com/ergochat/ergo/irc/utils" ) @@ -22,6 +23,7 @@ func newAPIHandler(server *Server) http.Handler { api.mux.HandleFunc("POST /v1/rehash", api.handleRehash) api.mux.HandleFunc("POST /v1/status", api.handleStatus) api.mux.HandleFunc("POST /v1/list", api.handleList) + api.mux.HandleFunc("POST /v1/defcon", api.handleDefcon) // use Ergo as a source of truth for authentication in other services: api.mux.HandleFunc("POST /v1/check_auth", api.handleCheckAuth) @@ -119,6 +121,34 @@ func (a *ergoAPI) handleRehash(w http.ResponseWriter, r *http.Request) { a.writeJSONResponse(response, w, r) } +type defconRequestResponse struct { + apiGenericResponse + Defcon int `json:"defcon"` +} + +func (a *ergoAPI) handleDefcon(w http.ResponseWriter, r *http.Request) { + var changeRequested uint32 + var request defconRequestResponse + // ignore errors or invalid values + if err := json.NewDecoder(r.Body).Decode(&request); err == nil { + if 1 <= request.Defcon && request.Defcon <= 5 { + changeRequested = uint32(request.Defcon) + } + } + if changeRequested != 0 { + a.server.SetDefcon(changeRequested) + message := fmt.Sprintf("API set DEFCON level to %d", changeRequested) + a.server.logger.Info("server", message) + a.server.snomasks.Send(sno.LocalAnnouncements, message) + } + a.writeJSONResponse( + defconRequestResponse{ + apiGenericResponse: apiGenericResponse{Success: true}, + Defcon: int(a.server.Defcon()), + }, w, r, + ) +} + type apiCheckAuthResponse struct { apiGenericResponse AccountName string `json:"accountName,omitempty"` diff --git a/irc/handlers.go b/irc/handlers.go index 5fc9d1da..b714f5db 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -942,7 +942,9 @@ func defconHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respo level, err := strconv.Atoi(msg.Params[0]) if err == nil && 1 <= level && level <= 5 { server.SetDefcon(uint32(level)) - server.snomasks.Send(sno.LocalAnnouncements, fmt.Sprintf("%s [%s] set DEFCON level to %d", client.Nick(), client.Oper().Name, level)) + message := fmt.Sprintf("%s [%s] set DEFCON level to %d", client.Nick(), client.Oper().Name, level) + server.logger.Info("server", message) + server.snomasks.Send(sno.LocalAnnouncements, message) } else { rb.Add(nil, server.name, ERR_UNKNOWNERROR, client.Nick(), msg.Command, client.t("Invalid DEFCON parameter")) return false