Eric Bower
·
2026-01-25
scp_hooks.go
1package prose
2
3import (
4 "fmt"
5 "path/filepath"
6 "strings"
7
8 "slices"
9
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 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 !shared.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 !shared.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) metaLxt(data *filehandlers.PostMetaData) error {
66 parsedText := shared.ListParseText(data.Text)
67
68 if parsedText.Title == "" {
69 data.Title = shared.ToUpper(data.Slug)
70 } else {
71 data.Title = parsedText.Title
72 }
73
74 data.Aliases = parsedText.Aliases
75 data.Tags = parsedText.Tags
76 data.Description = parsedText.Description
77
78 if parsedText.PublishAt != nil && !parsedText.PublishAt.IsZero() {
79 data.PublishAt = parsedText.PublishAt
80 }
81 data.Hidden = parsedText.Hidden
82
83 return nil
84}
85
86func (p *MarkdownHooks) metaMd(data *filehandlers.PostMetaData) error {
87 parsedText, err := shared.ParseText(data.Text)
88 if err != nil {
89 return fmt.Errorf("%s: %w", data.Filename, err)
90 }
91
92 if parsedText.Title == "" {
93 data.Title = shared.ToUpper(data.Slug)
94 } else {
95 data.Title = parsedText.Title
96 }
97
98 data.Aliases = parsedText.Aliases
99 data.Tags = parsedText.Tags
100 data.Description = parsedText.Description
101
102 if parsedText.PublishAt != nil && !parsedText.PublishAt.IsZero() {
103 data.PublishAt = parsedText.PublishAt
104 }
105 data.Hidden = parsedText.Hidden
106
107 return nil
108}
109
110func (p *MarkdownHooks) FileMeta(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) error {
111 ext := filepath.Ext(data.Filename)
112 switch ext {
113 case ".lxt":
114 err := p.metaLxt(data)
115 if err != nil {
116 return fmt.Errorf("%s: %w", data.Filename, err)
117 }
118 case ".md":
119 err := p.metaMd(data)
120 if err != nil {
121 return fmt.Errorf("%s: %w", data.Filename, err)
122 }
123 }
124
125 isHiddenFilename := slices.Contains(p.Cfg.HiddenPosts, data.Filename)
126 data.Hidden = data.Hidden || isHiddenFilename
127
128 return nil
129}