repos / pico

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

pico / pkg / apps / feeds
Eric Bower  ·  2025-08-08

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