init
This commit is contained in:
commit
0f70db9ead
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
result
|
||||||
|
pots
|
||||||
|
.direnv
|
26
flake.lock
Normal file
26
flake.lock
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1673226411,
|
||||||
|
"narHash": "sha256-b6cGb5Ln7Zy80YO66+cbTyGdjZKtkoqB/iIIhDX9gRA=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "aa1d74709f5dac623adb4d48fdfb27cc2c92a4d4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
38
flake.nix
Normal file
38
flake.nix
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
description = "pots: a tailscale pushover webhooker";
|
||||||
|
|
||||||
|
inputs.nixpkgs.url = "nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs }:
|
||||||
|
let
|
||||||
|
supportedSystems =
|
||||||
|
[ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
|
||||||
|
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||||
|
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
|
||||||
|
in {
|
||||||
|
packages = forAllSystems (system:
|
||||||
|
let pkgs = nixpkgsFor.${system};
|
||||||
|
in {
|
||||||
|
pots = pkgs.buildGoModule {
|
||||||
|
pname = "pots";
|
||||||
|
version = "v0.0.1";
|
||||||
|
src = ./.;
|
||||||
|
vendorHash = "sha256-pQpattmS9VmO3ZIQUFn66az8GSmB4IvYhTTCFn6SUmo=";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
defaultPackage = forAllSystems (system: self.packages.${system}.pots);
|
||||||
|
devShells = forAllSystems (system:
|
||||||
|
let pkgs = nixpkgsFor.${system};
|
||||||
|
in {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
shellHook = ''
|
||||||
|
PS1='\u@\h:\@; '
|
||||||
|
echo "Go `${pkgs.go}/bin/go version`"
|
||||||
|
'';
|
||||||
|
nativeBuildInputs = with pkgs; [ git go gopls go-tools jo jq ];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
155
main.go
Normal file
155
main.go
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogEntry struct {
|
||||||
|
Timestamp string `json:"timestamp"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Tailnet string `json:"tailnet"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data Data `json:"data"`
|
||||||
|
}
|
||||||
|
type Data struct {
|
||||||
|
NodeID string `json:"nodeID,omitempty"`
|
||||||
|
DeviceName string `json:"deviceName,omitempty"`
|
||||||
|
ManagedBy string `json:"managedBy,omitempty"`
|
||||||
|
Actor string `json:"actor,omitempty"`
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TSLogs []LogEntry
|
||||||
|
|
||||||
|
// Push represents a message sent to the Pushover api
|
||||||
|
type Push struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
User string `json:"user"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Device string `json:"device,omitempty"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
URLTitle string `json:"url_title,omitempty"`
|
||||||
|
Priority int `json:"priority,omitempty"`
|
||||||
|
Sound string `json:"sound,omitempty"`
|
||||||
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushResponse is a response from the Pushover api
|
||||||
|
type PushResponse struct {
|
||||||
|
Status int `json:"status,omitempty"`
|
||||||
|
Request string `json:"request,omitempty"`
|
||||||
|
User string `json:"user,omitempty"`
|
||||||
|
Errors []string `json:"errors,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type apiHandler struct{}
|
||||||
|
|
||||||
|
var (
|
||||||
|
appToken = os.Getenv("PUSHOVER_TOKEN")
|
||||||
|
userToken = os.Getenv("PUSHOVER_USER")
|
||||||
|
potsToken = os.Getenv("POTS_TOKEN")
|
||||||
|
client = *http.DefaultClient
|
||||||
|
)
|
||||||
|
|
||||||
|
func sendPush(p *Push) error {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
if err := json.NewEncoder(buf).Encode(p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", "https://api.pushover.net/1/messages.json", buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
var resBody PushResponse
|
||||||
|
if err = json.NewDecoder(res.Body).Decode(&resBody); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resBody.Errors) > 0 {
|
||||||
|
return fmt.Errorf("%s", strings.Join(resBody.Errors, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (apiHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
if req.Method != "POST" {
|
||||||
|
http.NotFound(w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token := path.Base(req.URL.Path)
|
||||||
|
if token != potsToken {
|
||||||
|
http.NotFound(w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(req.Body)
|
||||||
|
var tsLogs TSLogs
|
||||||
|
err := decoder.Decode(&tsLogs)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "invalid data", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range tsLogs {
|
||||||
|
var push = &Push{
|
||||||
|
Token: appToken,
|
||||||
|
User: userToken,
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
Title: t.Message,
|
||||||
|
Message: t.Type,
|
||||||
|
URL: t.Data.URL,
|
||||||
|
}
|
||||||
|
err := sendPush(push)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
listen := flag.String("listen", ":8888", "listen string")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.Handle("/api/", apiHandler{})
|
||||||
|
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
log.Println(req.URL.Path)
|
||||||
|
if req.URL.Path != "/" {
|
||||||
|
http.NotFound(w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, `<a href="https://suah.dev/pots">suah.dev/pots</a>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
s := &http.Server{
|
||||||
|
Addr: *listen,
|
||||||
|
Handler: mux,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Fatal(s.ListenAndServe())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user