Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add frigate auth headers #58

Merged
merged 2 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ type Config struct {
}

type Frigate struct {
Server string `fig:"server" validate:"required"`
Insecure bool `fig:"ignoressl" default:false`
WebAPI WebAPI `fig:"webapi"`
MQTT MQTT `fig:"mqtt"`
Cameras Cameras `fig:"cameras"`
Server string `fig:"server" validate:"required"`
Insecure bool `fig:"ignoressl" default:false`
Headers []map[string]string `fig:"headers"`
WebAPI WebAPI `fig:"webapi"`
MQTT MQTT `fig:"mqtt"`
Cameras Cameras `fig:"cameras"`
}

type WebAPI struct {
Expand Down Expand Up @@ -53,6 +54,7 @@ type Alerts struct {
SMTP SMTP `fig:"smtp"`
Telegram Telegram `fig:"telegram"`
Pushover Pushover `fig:"pushover"`
Nfty Nfty `fig:"nfty"`
}

type General struct {
Expand Down Expand Up @@ -110,6 +112,13 @@ type Pushover struct {
TTL int `fig:"ttl" default:0`
}

type Nfty struct {
Enabled bool `fig:"enabled" default:false`
Server string `fig:"server" default:""`
Topic string `fig:"topic" default:""`
Insecure bool `fig:"ignoressl" default:false`
}

type Monitor struct {
Enabled bool `fig:"enabled" default:false`
URL string `fig:"url" default:""`
Expand Down Expand Up @@ -301,6 +310,16 @@ func validateConfig() {
configErrors = append(configErrors, "Pushover TTL cannot be negative!")
}
}
if ConfigData.Alerts.Nfty.Enabled {
log.Print("Nfty alerting enabled.")
if ConfigData.Alerts.Nfty.Server == "" {
configErrors = append(configErrors, "No Nfty server specified!")
}
if ConfigData.Alerts.Nfty.Topic == "" {
configErrors = append(configErrors, "No Nfty topic specified!")
}

}

// Validate monitoring config
if ConfigData.Monitor.Enabled {
Expand Down
5 changes: 5 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## [v0.2.8](https://github.com/0x2142/frigate-notify/releases/tag/v0.2.8) - Upcoming Release

- Add support for notifications via [Nfty](https://frigate-notify.0x2142.com/config/#nfty)
- Add ability to send additional HTTP [headers](https://frigate-notify.0x2142.com/config/#frigate) to Frigate

## [v0.2.7](https://github.com/0x2142/frigate-notify/releases/tag/v0.2.7) - May 06 2024

- Allow changing default MQTT topic prefix via config
Expand Down
43 changes: 40 additions & 3 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,18 @@ Configuration snippets will be provided throughout this page. Feel free to copy
- IP or hostname of the Frigate NVR
- **ignoressl** (Optional - Default: `false`)
- Set to `true` to allow self-signed certificates
- **headers** (Optional)
- Send additional HTTP headers to Frigate
- Useful for things like authentication
- Header format: `Header: Value`
- Example: `Authorization: Basic abcd1234`

```yaml title="Config File Snippet"
frigate:
server: nvr.your.domain.tld
ignoressl: true
headers:
- Authorization: Basic abcd1234
```

### WebAPI
Expand Down Expand Up @@ -324,6 +331,31 @@ alerts:
ttl:
```

### Nfty

!!!note
The default Frigate-Notify alert message uses Markdown. Currently, Nfty only supports Markdown in the web browser. This means that mobile notifications will be shown in plain-text & display un-rendered Markdown syntax.

- **enabled** (Optional - Default: `false`)
- Set to `true` to enable alerting via Nfty
- **server** (Required)
- Full URL of the desired Nfty server
- Required if this alerting method is enabled
- **topic** (Required)
- Destination topic that will receive alert notifications
- Required if this alerting method is enabled
- **ignoressl** (Optional - Default: `false`)
- Set to `true` to allow self-signed certificates

```yaml title="Config File Snippet"
alerts:
nfty:
enabled: true
server: https://nfty.your.domain.tld
topic: frigate
ignoressl: true
```

## Monitor

If enabled, this application will check in with tools like [HealthChecks](https://github.com/healthchecks/healthchecks) or [Uptime Kuma](https://github.com/louislam/uptime-kuma) on a regular interval for health / status monitoring.
Expand All @@ -347,10 +379,8 @@ monitor:
ignoressl:
```


---


## Sample Config { data-search-exclude }

A full config file template has been provided below:
Expand All @@ -359,6 +389,7 @@ A full config file template has been provided below:
frigate:
server:
ignoressl:
headers:

webapi:
enabled:
Expand Down Expand Up @@ -431,9 +462,15 @@ alerts:
expire:
ttl:

nfty:
enabled: false
server:
topic:
ignoressl:

monitor:
enabled: false
url:
interval:
ignoressl:
```
```
5 changes: 3 additions & 2 deletions events/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ func CheckForEvents() {
log.Println("Checking for new events...")

// Query events
response, err := util.HTTPGet(url, config.ConfigData.Frigate.Insecure)
response, err := util.HTTPGet(url, config.ConfigData.Frigate.Insecure, config.ConfigData.Frigate.Headers...)
if err != nil {
log.Printf("Cannot get events from %s", url)
log.Printf("Error received: %s", err)
}

var events []Event
Expand Down Expand Up @@ -87,7 +88,7 @@ func CheckForEvents() {

// GetSnapshot downloads a snapshot from Frigate
func GetSnapshot(snapshotURL, eventID string) io.Reader {
response, err := util.HTTPGet(snapshotURL, config.ConfigData.Frigate.Insecure)
response, err := util.HTTPGet(snapshotURL, config.ConfigData.Frigate.Insecure, config.ConfigData.Frigate.Headers...)
if err != nil {
log.Println("Could not access snaphot. Error: ", err)
}
Expand Down
15 changes: 15 additions & 0 deletions example-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ frigate:
# Set to true if using SSL & a self-signed certificate
ignoressl: false

# List of HTTP headers to send to Frigate, in format Header: Value
headers:
# Example:
# - Authorization: Basic abcd1234

webapi:
# Set to true to enable event collection via the web API
enabled:
Expand Down Expand Up @@ -127,6 +132,16 @@ alerts:
# Optional message lifetime
ttl:

# Nfty Config
nfty:
# Set to true to enable alerting via
enabled: false
# URL of Nfty server
server:
# Nfty topic for notifications
topic:
# Set to true if using SSL & a self-signed certificate
ignoressl:

## App Monitoring
# Sends HTTP GET to provided URL for aliveness checks
Expand Down
3 changes: 3 additions & 0 deletions notifier/alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ func SendAlert(message, snapshotURL string, snapshot io.Reader, eventid string)
if config.ConfigData.Alerts.Pushover.Enabled {
SendPushoverMessage(message, bytes.NewReader(snap), eventid)
}
if config.ConfigData.Alerts.Nfty.Enabled {
SendNftyPush(message, bytes.NewReader(snap), eventid)
}
}
5 changes: 3 additions & 2 deletions notifier/gotify.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,14 @@ func SendGotifyPush(message, snapshotURL string, eventid string) {

data, err := json.Marshal(payload)
if err != nil {
log.Println("Event ID %v - Unable to build Gotify payload: ", eventid, err)
log.Printf("Event ID %v - Unable to build Gotify payload: %v", eventid, err)
return
}

gotifyURL := fmt.Sprintf("%s/message?token=%s&", config.ConfigData.Alerts.Gotify.Server, config.ConfigData.Alerts.Gotify.Token)

response, err := util.HTTPPost(gotifyURL, config.ConfigData.Alerts.Gotify.Insecure, data)
header := map[string]string{"Content-Type": "application/json"}
response, err := util.HTTPPost(gotifyURL, config.ConfigData.Alerts.Gotify.Insecure, data, header)
if err != nil {
log.Print("Failed to send Gotify notification: ", err)
return
Expand Down
45 changes: 45 additions & 0 deletions notifier/nfty.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package notifier

import (
"fmt"
"io"
"log"
"strings"

"github.com/0x2142/frigate-notify/config"
"github.com/0x2142/frigate-notify/util"
)

// SendNftyPush forwards alert messages to Nfty server
func SendNftyPush(message string, snapshot io.Reader, eventid string) {
NftyURL := fmt.Sprintf("%s/%s", config.ConfigData.Alerts.Nfty.Server, config.ConfigData.Alerts.Nfty.Topic)

// Set headers
var headers []map[string]string
headers = append(headers, map[string]string{"Content-Type": "text/markdown"})
headers = append(headers, map[string]string{"X-Title": config.ConfigData.Alerts.General.Title})

// Set action link to the recorded clip
clip := fmt.Sprintf("%s/api/events/%s/clip.mp4", config.ConfigData.Frigate.Server, eventid)
headers = append(headers, map[string]string{"X-Actions": "view, View Clip, " + clip + ", clear=true"})

var attachment []byte
if snapshot != nil {
headers = append(headers, map[string]string{"X-Filename": "snapshot.jpg"})
attachment, _ = io.ReadAll(snapshot)
} else {
message += "\n\nNo snapshot saved."
}

// Escape newlines in message
message = strings.ReplaceAll(message, "\n", "\\n")
headers = append(headers, map[string]string{"X-Message": message})

_, err := util.HTTPPost(NftyURL, config.ConfigData.Alerts.Nfty.Insecure, attachment, headers...)
if err != nil {
log.Print("Failed to send Nfty notification: ", err)
return
}

log.Printf("Event ID %v - Nfty alert sent", eventid)
}
31 changes: 28 additions & 3 deletions util/httpclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package util
import (
"bytes"
"crypto/tls"
"errors"
"io"
"net/http"
"strconv"
"time"
)

// HTTPGet is a simple HTTP client function to return page body
func HTTPGet(url string, insecure bool) ([]byte, error) {
func HTTPGet(url string, insecure bool, headers ...map[string]string) ([]byte, error) {

// New HTTP Client
client := http.Client{Timeout: 10 * time.Second}
Expand All @@ -25,6 +27,16 @@ func HTTPGet(url string, insecure bool) ([]byte, error) {
return nil, err
}

// Add headers
if len(headers) > 0 {
for _, h := range headers {
for k, v := range h {
req.Header.Add(k, v)
}

}
}

// Send HTTP GET
response, err := client.Do(req)
if err != nil {
Expand All @@ -38,12 +50,16 @@ func HTTPGet(url string, insecure bool) ([]byte, error) {
return nil, err
}

if response.StatusCode != 200 {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}

return body, nil
}

// HTTPPost performs an HTTP POST to the target URL
// and includes auth parameters, ignoring certificates, etc
func HTTPPost(url string, insecure bool, payload []byte) ([]byte, error) {
func HTTPPost(url string, insecure bool, payload []byte, headers ...map[string]string) ([]byte, error) {
// New HTTP Client
client := http.Client{Timeout: 10 * time.Second}

Expand All @@ -58,7 +74,16 @@ func HTTPPost(url string, insecure bool, payload []byte) ([]byte, error) {
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")

// Add headers
if len(headers) > 0 {
for _, h := range headers {
for k, v := range h {
req.Header.Add(k, v)
}

}
}

// Send HTTP POST
response, err := client.Do(req)
Expand Down
Loading