diff --git a/.gitignore b/.gitignore index d94ab38..8e58869 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.db -beretta* +data +bin diff --git a/Containerfile b/Containerfile new file mode 100644 index 0000000..e47e4e0 --- /dev/null +++ b/Containerfile @@ -0,0 +1,16 @@ +FROM docker.io/golang:1.24 AS builder +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download +COPY src src +RUN GOOS=linux go build -o beretta src/*.go +# FROM docker.io/alpine:3 +FROM docker.io/debian:bookworm-slim + +RUN apt-get update && apt-get install -y \ + ca-certificates \ + sqlite3 \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=builder /app/beretta /beretta +ENTRYPOINT ["/beretta"] diff --git a/Makefile b/Makefile index 54bdd5e..b0f19e6 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,11 @@ +build: + go build -o bin/beretta src/*.go + run: go run src/*.go -build: - go build -o beretta src/*.go +build-container: + podman build -t beretta . clean: rm beretta diff --git a/beretta.service b/beretta.service new file mode 100644 index 0000000..85420f1 --- /dev/null +++ b/beretta.service @@ -0,0 +1,14 @@ +[Unit] +Description=Beretta Service +After=network.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/beretta +Restart=on-failure +RestartSec=5 +User=beretta +WorkingDirectory=/usr/local/bin + +[Install] +WantedBy=multi-user.target diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..275408c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +services: + beretta: + image: beretta:latest + name: beretta-server + build: + context: . + command: > + -config /data/beretta.json + -db /data/cache.db + volumes: + - ./data:/data:Z diff --git a/beretta.json b/src/beretta.json similarity index 80% rename from beretta.json rename to src/beretta.json index 92042bf..e53438f 100644 --- a/beretta.json +++ b/src/beretta.json @@ -10,6 +10,6 @@ } ], "interval": 1200, - "ntfy_topic": "hee4waeNguch" + "ntfy_topic": "{{.TopicToken}}" } diff --git a/src/main.go b/src/main.go index fa55a33..0561a64 100644 --- a/src/main.go +++ b/src/main.go @@ -1,12 +1,18 @@ package main import ( + _ "embed" "flag" "log" "math/rand" + "os" + "text/template" "time" ) +//go:embed beretta.json +var defconfig string + type RepoUpdate struct { repo Repo Tag string @@ -22,7 +28,7 @@ func notifierThread(c chan RepoUpdate, n Notifier) { } func repoThread(c chan RepoUpdate, repo Repo, avgInterval int, db Db) { - log.Println("Checking", repo.IdTuple()) + log.Println("Checking: ", repo.IdTuple()) tag, err := repo.GetLatestTag() isNewVersion, err_2 := db.CheckAndStore(repo.IdTuple(), tag.Name) @@ -33,6 +39,7 @@ func repoThread(c chan RepoUpdate, repo Repo, avgInterval int, db Db) { } else if isNewVersion { c <- RepoUpdate{repo, tag.Name} } + log.Println("Checking done: ", repo.IdTuple()) sleepTime := time.Duration(rand.Intn(avgInterval)) * time.Second time.Sleep(sleepTime) @@ -41,25 +48,54 @@ func repoThread(c chan RepoUpdate, repo Repo, avgInterval int, db Db) { } func main() { - - conf := flag.String("config", "./beretta.json", "The path to the config file") + conf_path := flag.String("config", "beretta.json", "The path to the config file") + db_path := flag.String("db", "cache.db", "The path to the cache database") flag.Parse() - log.Printf("Using config path: %s", *conf) + tmpl, err := template.New("config").Parse(defconfig) + if err != nil { + panic(err) + } + + randtk, err := randToken(8) + if err != nil { + panic(err) + } + + data := map[string]any{ + "TopicToken": randtk, + } + + if _, err := os.Stat(*conf_path); os.IsNotExist(err) { + log.Printf("Writing new config file: %s", *conf_path) + file, err := os.Create(*conf_path) + if err != nil { + panic(err) + } + err = tmpl.Execute(file, data) + if err != nil { + log.Fatalf("Error writing config file: %s", err) + return + } + } + + log.Printf("Using config path: %s", *conf_path) + log.Printf("Using db path: %s", *db_path) // Load configuration - config, err := loadConfig(*conf) + config, err := loadConfig(*conf_path) if err != nil { log.Fatalf("Failed to load configuration: %v", err) } // Create database - db, err := NewSQLiteDb("tags.db") + db, err := NewSQLiteDb(*db_path) if err != nil { log.Fatalf("Failed to create database: %v", err) } notifier := NtfyNotifier{Topic: config.NtfyTopic} + log.Printf("Sending notifications to: %s", notifier.Url()) c := make(chan RepoUpdate) diff --git a/src/random_token.go b/src/random_token.go new file mode 100644 index 0000000..a8d2b96 --- /dev/null +++ b/src/random_token.go @@ -0,0 +1,14 @@ +package main + +import ( + "crypto/rand" + "encoding/hex" +) + +func randToken(n int) (string, error) { + bytes := make([]byte, n) + if _, err := rand.Read(bytes); err != nil { + return "", err + } + return hex.EncodeToString(bytes), nil +} diff --git a/src/types.go b/src/types.go index 59074f2..e66f97f 100644 --- a/src/types.go +++ b/src/types.go @@ -39,6 +39,7 @@ func loadConfig(file string) (Config, error) { type Notifier interface { Notify(repo Repo, tag string) bool + Url() string } type NtfyNotifier struct { @@ -46,10 +47,10 @@ type NtfyNotifier struct { } func (n NtfyNotifier) Notify(repo Repo, tag string) bool { - ntfyURL := fmt.Sprintf("https://ntfy.sh/%s", n.Topic) + // ntfyURL := fmt.Sprintf("https://ntfy.sh/%s", n.Topic) message := fmt.Sprintf("New release for %s/%s: %s", repo.Owner(), repo.Name(), tag) - _, err := http.Post(ntfyURL, "text/plain", bytes.NewBuffer([]byte(message))) + _, err := http.Post(n.Url(), "text/plain", bytes.NewBuffer([]byte(message))) if err != nil { log.Printf("Failed to send notification: %v", err) return false @@ -57,3 +58,7 @@ func (n NtfyNotifier) Notify(repo Repo, tag string) bool { log.Printf("Notification sent: %s", message) return true } + +func (n NtfyNotifier) Url() string { + return fmt.Sprintf("https://ntfy.sh/%s", n.Topic) +}