repos / pico

pico services mono repo
git clone https://github.com/picosh/pico.git

commit
bb7b4f2
parent
b2aa9b3
author
Eric Bower
date
2026-01-08 22:13:26 -0500 EST
feat(pipe): web based rss feed
2 files changed,  +51, -12
M pkg/apps/pipe/api.go
+41, -2
 1@@ -16,6 +16,7 @@ import (
 2 
 3 	"github.com/google/uuid"
 4 	"github.com/gorilla/websocket"
 5+	"github.com/picosh/pico/pkg/db"
 6 	"github.com/picosh/pico/pkg/db/postgres"
 7 	"github.com/picosh/pico/pkg/shared"
 8 	"github.com/picosh/utils/pipe"
 9@@ -386,10 +387,48 @@ func handlePipe() http.HandlerFunc {
10 	}
11 }
12 
13-func createMainRoutes(staticRoutes []shared.Route) []shared.Route {
14+func rssHandler(cfg *shared.ConfigSite, dbpool db.DB) http.HandlerFunc {
15+	return func(w http.ResponseWriter, r *http.Request) {
16+		apiToken, _ := url.PathUnescape(shared.GetField(r, 0))
17+		user, err := dbpool.FindUserByToken(apiToken)
18+		if err != nil {
19+			cfg.Logger.Error(
20+				"could not find user for token",
21+				"err", err.Error(),
22+				"token", apiToken,
23+			)
24+			http.Error(w, "invalid token", http.StatusNotFound)
25+			return
26+		}
27+		rss, err := MonitorRss(dbpool, user, cfg.Domain)
28+		if err != nil {
29+			cfg.Logger.Error(
30+				"error generating monitor rss feed",
31+				"err", err,
32+				"token", apiToken,
33+			)
34+			http.Error(w, "error generating monitor rss feed", http.StatusInternalServerError)
35+			return
36+		}
37+
38+		_, err = w.Write([]byte(rss))
39+		if err != nil {
40+			cfg.Logger.Error(
41+				"error with rss response writer",
42+				"err", err,
43+				"token", apiToken,
44+			)
45+			http.Error(w, "error generating monitor rss feederror with rss response writer", http.StatusInternalServerError)
46+			return
47+		}
48+	}
49+}
50+
51+func createMainRoutes(staticRoutes []shared.Route, cfg *shared.ConfigSite, dbpool db.DB) []shared.Route {
52 	routes := []shared.Route{
53 		shared.NewRoute("GET", "/", shared.CreatePageHandler("html/marketing.page.tmpl")),
54 		shared.NewRoute("GET", "/check", shared.CheckHandler),
55+		shared.NewRoute("GET", "/rss/(.+)", rssHandler(cfg, dbpool)),
56 		shared.NewRoute("GET", "/_metrics", promhttp.Handler().ServeHTTP),
57 	}
58 
59@@ -428,7 +467,7 @@ func StartApiServer() {
60 		staticRoutes = shared.CreatePProfRoutes(staticRoutes)
61 	}
62 
63-	mainRoutes := createMainRoutes(staticRoutes)
64+	mainRoutes := createMainRoutes(staticRoutes, cfg, db)
65 	subdomainRoutes := staticRoutes
66 
67 	info := shared.NewPicoPipeClient()
M pkg/apps/pipe/cli.go
+10, -10
 1@@ -93,7 +93,8 @@ func Middleware(handler *CliHandler) pssh.SSHServerMiddleware {
 2 				}
 3 				return next(sesh)
 4 			case "rss":
 5-				err := handler.rss(cliCmd, user)
 6+				rss, err := MonitorRss(handler.DBPool, user, handler.Cfg.Domain)
 7+				_, _ = fmt.Fprintln(sesh, rss)
 8 				if err != nil {
 9 					logger.Error("rss cmd", "err", err)
10 					sesh.Fatal(err)
11@@ -542,20 +543,20 @@ func parseDuration(s string) (time.Duration, error) {
12 	return time.ParseDuration(s)
13 }
14 
15-func (handler *CliHandler) rss(cmd *CliCmd, user *db.User) error {
16+func MonitorRss(dbpool db.DB, user *db.User, domain string) (string, error) {
17 	if user == nil {
18-		return fmt.Errorf("access denied")
19+		return "", fmt.Errorf("access denied")
20 	}
21 
22-	monitors, err := handler.DBPool.FindPipeMonitorsByUser(user.ID)
23+	monitors, err := dbpool.FindPipeMonitorsByUser(user.ID)
24 	if err != nil {
25-		return fmt.Errorf("failed to fetch monitors: %w", err)
26+		return "", fmt.Errorf("failed to fetch monitors: %w", err)
27 	}
28 
29 	now := time.Now()
30 	feed := &feeds.Feed{
31 		Title:       fmt.Sprintf("Pipe Monitors for %s", user.Name),
32-		Link:        &feeds.Link{Href: fmt.Sprintf("https://%s", handler.Cfg.Domain)},
33+		Link:        &feeds.Link{Href: fmt.Sprintf("https://%s", domain)},
34 		Description: "Alerts for pipe monitor status changes",
35 		Author:      &feeds.Author{Name: user.Name},
36 		Created:     now,
37@@ -567,7 +568,7 @@ func (handler *CliHandler) rss(cmd *CliCmd, user *db.User) error {
38 			item := &feeds.Item{
39 				Id:          fmt.Sprintf("%s-%s-%d", user.ID, m.Topic, now.Unix()),
40 				Title:       fmt.Sprintf("ALERT: %s is unhealthy", m.Topic),
41-				Link:        &feeds.Link{Href: fmt.Sprintf("https://%s", handler.Cfg.Domain)},
42+				Link:        &feeds.Link{Href: fmt.Sprintf("https://%s", domain)},
43 				Description: err.Error(),
44 				Created:     now,
45 				Updated:     now,
46@@ -580,11 +581,10 @@ func (handler *CliHandler) rss(cmd *CliCmd, user *db.User) error {
47 
48 	rss, err := feed.ToRss()
49 	if err != nil {
50-		return fmt.Errorf("failed to generate RSS: %w", err)
51+		return "", fmt.Errorf("failed to generate RSS: %w", err)
52 	}
53 
54-	_, _ = fmt.Fprint(cmd.sesh, rss)
55-	return nil
56+	return rss, nil
57 }
58 
59 func (handler *CliHandler) pub(cmd *CliCmd, topic string, clientID string) error {