repos / pico

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

pico / pkg / apps / feeds
Eric Bower  ·  2026-01-25

scp_hooks.go

 1package feeds
 2
 3import (
 4	"errors"
 5	"fmt"
 6	"net/url"
 7	"strings"
 8
 9	"github.com/adhocore/gronx"
10	"github.com/picosh/pico/pkg/db"
11	"github.com/picosh/pico/pkg/filehandlers"
12	"github.com/picosh/pico/pkg/pssh"
13	"github.com/picosh/pico/pkg/shared"
14)
15
16type FeedHooks struct {
17	Cfg *shared.ConfigSite
18	Db  db.DB
19}
20
21func (p *FeedHooks) FileValidate(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) (bool, error) {
22	if !shared.IsTextFile(string(data.Text)) {
23		err := fmt.Errorf(
24			"WARNING: (%s) invalid file must be plain text (utf-8), skipping",
25			data.Filename,
26		)
27		return false, err
28	}
29
30	if !shared.IsExtAllowed(data.Filename, p.Cfg.AllowedExt) {
31		extStr := strings.Join(p.Cfg.AllowedExt, ",")
32		err := fmt.Errorf(
33			"WARNING: (%s) invalid file, format must be (%s), skipping",
34			data.Filename,
35			extStr,
36		)
37		return false, err
38	}
39
40	// Because we need to support sshfs, sftp runs our Write handler twice
41	// and on the first pass we do not have access to the file data.
42	// In that case we should skip the parsing validation
43	if data.Text == "" {
44		return true, nil
45	}
46
47	parsed := shared.ListParseText(string(data.Text))
48	if parsed.Email == "" {
49		return false, fmt.Errorf("ERROR: no email variable detected for %s, check the format of your file, skipping", data.Filename)
50	}
51
52	if parsed.DigestInterval != "" {
53		return false, fmt.Errorf("ERROR: `digest_interval` is deprecated; use `cron`: https://pico.sh/feeds#cron")
54	}
55
56	if parsed.Cron != "" {
57		if !gronx.IsValid(parsed.Cron) {
58			return false, fmt.Errorf("ERROR: `cron` is invalid, reference: https://github.com/adhocore/gronx?tab=readme-ov-file#cron-expression")
59		}
60	}
61
62	var allErr error
63	for _, txt := range parsed.Items {
64		u := ""
65		if txt.IsText {
66			u = txt.Value
67		} else if txt.IsURL {
68			u = string(txt.URL)
69		}
70
71		_, err := url.Parse(u)
72		if err != nil {
73			allErr = errors.Join(allErr, fmt.Errorf("%s: %w", u, err))
74			continue
75		}
76	}
77	if allErr != nil {
78		return false, fmt.Errorf("ERROR: some urls provided were invalid check the format of your file, skipping: %w", allErr)
79	}
80
81	return true, nil
82}
83
84func (p *FeedHooks) FileMeta(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) error {
85	return nil
86}