Eric Bower
·
2025-09-07
cli.go
1package feeds
2
3import (
4 "fmt"
5 "text/tabwriter"
6 "time"
7
8 "github.com/adhocore/gronx"
9 "github.com/picosh/pico/pkg/db"
10 "github.com/picosh/pico/pkg/pssh"
11 "github.com/picosh/pico/pkg/shared"
12)
13
14func Middleware(dbpool db.DB, cfg *shared.ConfigSite) pssh.SSHServerMiddleware {
15 return func(next pssh.SSHServerHandler) pssh.SSHServerHandler {
16 return func(sesh *pssh.SSHServerConnSession) error {
17 args := sesh.Command()
18
19 logger := pssh.GetLogger(sesh)
20 user := pssh.GetUser(sesh)
21
22 if user == nil {
23 err := fmt.Errorf("user not found")
24 _, _ = fmt.Fprintln(sesh.Stderr(), err)
25 return err
26 }
27
28 logger = shared.LoggerWithUser(logger, user)
29
30 cmd := "help"
31 if len(args) > 0 {
32 cmd = args[0]
33 }
34
35 switch cmd {
36 case "help":
37 _, _ = fmt.Fprintf(sesh, "Commands: [help, ls, rm, print, run]\r\n\r\n")
38 writer := tabwriter.NewWriter(sesh, 0, 0, 1, ' ', tabwriter.TabIndent)
39 _, _ = fmt.Fprintln(writer, "Cmd\tDesc")
40 _, _ = fmt.Fprintf(
41 writer,
42 "%s\t%s\r\n",
43 "help", "this help text",
44 )
45 _, _ = fmt.Fprintf(
46 writer,
47 "%s\t%s\r\n",
48 "ls", "list feed digest posts with metadata",
49 )
50 _, _ = fmt.Fprintf(
51 writer,
52 "%s\t%s\r\n",
53 "rm {filename}", "removes feed digest post",
54 )
55 _, _ = fmt.Fprintf(
56 writer,
57 "%s\t%s\r\n",
58 "print {filename}", "prints the feed digest file",
59 )
60 _, _ = fmt.Fprintf(
61 writer,
62 "%s\t%s\r\n",
63 "run {filename}", "runs the feed digest post immediately, ignoring cron timer",
64 )
65 return writer.Flush()
66 case "ls":
67 posts, err := dbpool.FindPostsForUser(&db.Pager{Page: 0, Num: 1000}, user.ID, "feeds")
68 if err != nil {
69 _, _ = fmt.Fprintln(sesh.Stderr(), err)
70 return err
71 }
72
73 if len(posts.Data) == 0 {
74 _, _ = fmt.Fprintln(sesh, "no posts found")
75 }
76
77 writer := tabwriter.NewWriter(sesh, 0, 0, 1, ' ', tabwriter.TabIndent)
78 _, _ = fmt.Fprintln(writer, "Filename\tLast Digest\tNext Digest\tCron\tFailed Attempts")
79 for _, post := range posts.Data {
80 parsed := shared.ListParseText(post.Text)
81
82 nextDigest := ""
83 cron := parsed.Cron
84 if parsed.DigestInterval != "" {
85 cron = DigestIntervalToCron(parsed.DigestInterval)
86 }
87 nd, _ := gronx.NextTickAfter(cron, DateToMin(time.Now()), true)
88 nextDigest = nd.Format(time.RFC3339)
89 last := post.Data.LastDigest
90 lastStr := "never"
91 if last != nil {
92 lastStr = last.Format(time.RFC3339)
93 }
94 _, _ = fmt.Fprintf(
95 writer,
96 "%s\t%s\t%s\t%s\t%d/10\r\n",
97 post.Filename,
98 lastStr,
99 nextDigest,
100 cron,
101 post.Data.Attempts,
102 )
103 }
104 return writer.Flush()
105 case "rm":
106 filename := args[1]
107 _, _ = fmt.Fprintf(sesh, "removing digest post %s\r\n", filename)
108 write := false
109 if len(args) > 2 {
110 writeRaw := args[2]
111 if writeRaw == "--write" {
112 write = true
113 }
114 }
115
116 post, err := dbpool.FindPostWithFilename(filename, user.ID, "feeds")
117 if err != nil {
118 _, _ = fmt.Fprintln(sesh.Stderr(), err)
119 return err
120 }
121 logger.Info("rm cmd", "filename", filename, "write", write)
122 if write {
123 err = dbpool.RemovePosts([]string{post.ID})
124 if err != nil {
125 _, _ = fmt.Fprintln(sesh.Stderr(), err)
126 }
127 }
128 _, _ = fmt.Fprintf(sesh, "digest post removed %s\r\n", filename)
129 if !write {
130 _, _ = fmt.Fprintln(sesh, "WARNING: *must* append with `--write` for the changes to persist.")
131 }
132 return err
133 case "print":
134 if len(args) < 2 {
135 err := fmt.Errorf("must provide filename of post to run")
136 _, _ = fmt.Fprintln(sesh.Stderr(), err)
137 return err
138 }
139 filename := args[1]
140 post, err := dbpool.FindPostWithFilename(filename, user.ID, "feeds")
141 if err != nil {
142 _, _ = fmt.Fprintln(sesh.Stderr(), err)
143 return err
144 }
145 _, _ = fmt.Fprintf(sesh, "%s\n", post.Text)
146 return nil
147 case "run":
148 if len(args) < 2 {
149 err := fmt.Errorf("must provide filename of post to run")
150 _, _ = fmt.Fprintln(sesh.Stderr(), err)
151 return err
152 }
153 filename := args[1]
154 post, err := dbpool.FindPostWithFilename(filename, user.ID, "feeds")
155 if err != nil {
156 _, _ = fmt.Fprintln(sesh.Stderr(), err)
157 return err
158 }
159 _, _ = fmt.Fprintf(sesh, "running feed post: %s\r\n", filename)
160 logger.Info("run cmd", "filename", filename)
161 fetcher := NewFetcher(dbpool, cfg)
162 err = fetcher.RunPost(logger, user, post, true, time.Now().UTC())
163 if err != nil {
164 _, _ = fmt.Fprintln(sesh.Stderr(), err)
165 }
166 return err
167 }
168
169 return next(sesh)
170 }
171 }
172}