repos / pico

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

pico / pkg / apps / prose
Eric Bower  ·  2025-07-21

scp_hooks.go

 1package prose
 2
 3import (
 4	"fmt"
 5	"strings"
 6
 7	"slices"
 8
 9	"github.com/picosh/pico/pkg/db"
10	"github.com/picosh/pico/pkg/filehandlers"
11	"github.com/picosh/pico/pkg/pssh"
12	"github.com/picosh/pico/pkg/shared"
13	"github.com/picosh/utils"
14	pipeUtil "github.com/picosh/utils/pipe"
15)
16
17type MarkdownHooks struct {
18	Cfg  *shared.ConfigSite
19	Db   db.DB
20	Pipe *pipeUtil.ReconnectReadWriteCloser
21}
22
23func (p *MarkdownHooks) FileValidate(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) (bool, error) {
24	if !utils.IsTextFile(data.Text) {
25		err := fmt.Errorf(
26			"ERROR: (%s) invalid file must be plain text (utf-8), skipping",
27			data.Filename,
28		)
29		return false, err
30	}
31
32	fp := strings.Replace(data.Filename, "/", "", 1)
33	// special styles css file we want to permit but no other css file.
34	// sometimes the directory is provided in the filename, so we want to
35	// remove that before we perform this check.
36	if fp == "_styles.css" {
37		return true, nil
38	}
39	// allow users to upload robots file
40	if fp == "robots.txt" {
41		return true, nil
42	}
43
44	if !utils.IsExtAllowed(data.Filename, p.Cfg.AllowedExt) {
45		extStr := strings.Join(p.Cfg.AllowedExt, ",")
46		err := fmt.Errorf(
47			"ERROR: (%s) invalid file, format must be (%s), skipping",
48			data.Filename,
49			extStr,
50		)
51		return false, err
52	}
53
54	if data.FileSize > MAX_FILE_SIZE {
55		return false, fmt.Errorf(
56			"ERROR: file (%s) has exceeded maximum file size (%d bytes)",
57			data.Filename,
58			MAX_FILE_SIZE,
59		)
60	}
61
62	return true, nil
63}
64
65func (p *MarkdownHooks) FileMeta(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) error {
66	parsedText, err := shared.ParseText(data.Text)
67	if err != nil {
68		return fmt.Errorf("%s: %w", data.Filename, err)
69	}
70
71	if parsedText.Title == "" {
72		data.Title = utils.ToUpper(data.Slug)
73	} else {
74		data.Title = parsedText.Title
75	}
76
77	data.Aliases = parsedText.Aliases
78	data.Tags = parsedText.Tags
79	data.Description = parsedText.Description
80
81	if parsedText.PublishAt != nil && !parsedText.PublishAt.IsZero() {
82		data.PublishAt = parsedText.PublishAt
83	}
84
85	isHiddenFilename := slices.Contains(p.Cfg.HiddenPosts, data.Filename)
86	data.Hidden = parsedText.Hidden || isHiddenFilename
87
88	return nil
89}