using System.Text; using API.APIEndpointRecords; using API.Schema; using API.Schema.NotificationConnectors; using Asp.Versioning; using Microsoft.AspNetCore.Mvc; using static Microsoft.AspNetCore.Http.StatusCodes; namespace API.Controllers; [ApiVersion(2)] [ApiController] [Produces("application/json")] [Route("v{v:apiVersion}/[controller]")] public class NotificationConnectorController(PgsqlContext context) : Controller { /// /// Gets all configured Notification-Connectors /// /// [HttpGet] [ProducesResponseType(Status200OK, "application/json")] public IActionResult GetAllConnectors() { NotificationConnector[] ret = context.NotificationConnectors.ToArray(); return Ok(ret); } /// /// Returns Notification-Connector with requested ID /// /// Notification-Connector-ID /// /// NotificationConnector with ID not found [HttpGet("{NotificationConnectorId}")] [ProducesResponseType(Status200OK, "application/json")] [ProducesResponseType(Status404NotFound)] public IActionResult GetConnector(string NotificationConnectorId) { NotificationConnector? ret = context.NotificationConnectors.Find(NotificationConnectorId); return (ret is not null) switch { true => Ok(ret), false => NotFound() }; } /// /// Creates a new REST-Notification-Connector /// /// Formatting placeholders: "%title" and "%text" can be placed in url, header-values and body and will be replaced when notifications are sent /// Notification-Connector /// ID of new connector /// A NotificationConnector with name already exists /// Error during Database Operation [HttpPut] [ProducesResponseType(Status201Created, "application/json")] [ProducesResponseType(Status409Conflict)] [ProducesResponseType(Status500InternalServerError, "text/plain")] public IActionResult CreateConnector([FromBody]NotificationConnector notificationConnector) { if (context.NotificationConnectors.Find(notificationConnector.Name) is not null) return Conflict(); try { context.NotificationConnectors.Add(notificationConnector); context.SaveChanges(); return Created(notificationConnector.Name, notificationConnector); } catch (Exception e) { return StatusCode(500, e.Message); } } /// /// Creates a new Gotify-Notification-Connector /// /// Priority needs to be between 0 and 10 /// ID of new connector /// /// A NotificationConnector with name already exists /// Error during Database Operation [HttpPut("Gotify")] [ProducesResponseType(Status201Created, "application/json")] [ProducesResponseType(Status400BadRequest)] [ProducesResponseType(Status409Conflict)] [ProducesResponseType(Status500InternalServerError, "text/plain")] public IActionResult CreateGotifyConnector([FromBody]GotifyRecord gotifyData) { if(!gotifyData.Validate()) return BadRequest(); NotificationConnector gotifyConnector = new NotificationConnector(TokenGen.CreateToken("Gotify"), gotifyData.endpoint, new Dictionary() { { "X-Gotify-Key", gotifyData.appToken } }, "POST", $"{{\"message\": \"%text\", \"title\": \"%title\", \"priority\": {gotifyData.priority}}}"); return CreateConnector(gotifyConnector); } /// /// Creates a new Ntfy-Notification-Connector /// /// Priority needs to be between 1 and 5 /// ID of new connector /// /// A NotificationConnector with name already exists /// Error during Database Operation [HttpPut("Ntfy")] [ProducesResponseType(Status201Created, "application/json")] [ProducesResponseType(Status400BadRequest)] [ProducesResponseType(Status409Conflict)] [ProducesResponseType(Status500InternalServerError, "text/plain")] public IActionResult CreateNtfyConnector([FromBody]NtfyRecord ntfyRecord) { if(!ntfyRecord.Validate()) return BadRequest(); string authHeader = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ntfyRecord.username}:{ntfyRecord.password}")); string auth = Convert.ToBase64String(Encoding.UTF8.GetBytes(authHeader)).Replace("=",""); NotificationConnector ntfyConnector = new (TokenGen.CreateToken("Ntfy"), $"{ntfyRecord.endpoint}?auth={auth}", new Dictionary() { {"Title", "%title"}, {"Priority", ntfyRecord.priority.ToString()}, }, "POST", "%text"); return CreateConnector(ntfyConnector); } /// /// Creates a new Lunasea-Notification-Connector /// /// https://docs.lunasea.app/lunasea/notifications/custom-notifications for id. Either device/:device_id or user/:user_id /// ID of new connector /// /// A NotificationConnector with name already exists /// Error during Database Operation [HttpPut("Lunasea")] [ProducesResponseType(Status201Created, "application/json")] [ProducesResponseType(Status400BadRequest)] [ProducesResponseType(Status409Conflict)] [ProducesResponseType(Status500InternalServerError, "text/plain")] public IActionResult CreateLunaseaConnector([FromBody]LunaseaRecord lunaseaRecord) { if(!lunaseaRecord.Validate()) return BadRequest(); NotificationConnector lunaseaConnector = new (TokenGen.CreateToken("Lunasea"), $"https://notify.lunasea.app/v1/custom/{lunaseaRecord.id}", new Dictionary(), "POST", "{\"title\": \"%title\", \"body\": \"%text\"}"); return CreateConnector(lunaseaConnector); } /// /// Creates a new Pushover-Notification-Connector /// /// https://pushover.net/api /// ID of new connector /// /// A NotificationConnector with name already exists /// Error during Database Operation [HttpPut("Pushover")] [ProducesResponseType(Status201Created, "application/json")] [ProducesResponseType(Status400BadRequest)] [ProducesResponseType(Status409Conflict)] [ProducesResponseType(Status500InternalServerError, "text/plain")] public IActionResult CreatePushoverConnector([FromBody]PushoverRecord pushoverRecord) { if(!pushoverRecord.Validate()) return BadRequest(); NotificationConnector pushoverConnector = new (TokenGen.CreateToken("Pushover"), $"https://api.pushover.net/1/messages.json", new Dictionary(), "POST", $"{{\"token\": \"{pushoverRecord.apptoken}\", \"user\": \"{pushoverRecord.user}\", \"message:\":\"%text\", \"%title\" }}"); return CreateConnector(pushoverConnector); } /// /// Deletes the Notification-Connector with the requested ID /// /// Notification-Connector-ID /// /// NotificationConnector with ID not found /// Error during Database Operation [HttpDelete("{NotificationConnectorId}")] [ProducesResponseType(Status200OK)] [ProducesResponseType(Status404NotFound)] [ProducesResponseType(Status500InternalServerError, "text/plain")] public IActionResult DeleteConnector(string NotificationConnectorId) { try { NotificationConnector? ret = context.NotificationConnectors.Find(NotificationConnectorId); if(ret is null) return NotFound(); context.Remove(ret); context.SaveChanges(); return Ok(); } catch (Exception e) { return StatusCode(500, e.Message); } } }