Antonio Mika
·
2026-10-11
1package prose
2
3import (
4 "fmt"
5 "log/slog"
6
7 "github.com/picosh/pico/pkg/db"
8 "github.com/picosh/pico/pkg/pssh"
9 sendutils "github.com/picosh/pico/pkg/send/utils"
10 "github.com/picosh/pico/pkg/storage"
11)
12
13type ctxFeatureFlagKey struct{}
14
15func getFeatureFlag(s *pssh.SSHServerConnSession) *db.FeatureFlag {
16 v := s.Context().Value(ctxFeatureFlagKey{})
17 if v == nil {
18 return nil
19 }
20 ff := s.Context().Value(ctxFeatureFlagKey{}).(*db.FeatureFlag)
21 return ff
22}
23
24func setFeatureFlag(s *pssh.SSHServerConnSession, ff *db.FeatureFlag) {
25 s.SetValue(ctxFeatureFlagKey{}, ff)
26}
27
28type UploadHandler struct {
29 *fileRouter
30 Cfg *Config
31 DB DB
32}
33
34var _ sendutils.CopyFromClientHandler = &UploadHandler{}
35var _ sendutils.CopyFromClientHandler = (*UploadHandler)(nil)
36
37func NewUploadHandler(cfg *Config, dbpool DB, fileMap map[string]ReadWriteHandler) *UploadHandler {
38 router := newFileRouter(cfg.Space, fileMap)
39 return &UploadHandler{
40 fileRouter: router,
41 Cfg: cfg,
42 DB: dbpool,
43 }
44}
45
46// NewFileHandler routes prose's text files to posts and the rest to images.
47func NewFileHandler(cfg *Config, dbpool DB, st storage.StorageServe) *UploadHandler {
48 // One post handler serves every text extension, so a post can be
49 // renamed in place from one to another.
50 posts := &postUploader{Cfg: cfg, DB: dbpool}
51 return NewUploadHandler(cfg, dbpool, map[string]ReadWriteHandler{
52 ".md": posts,
53 ".txt": posts,
54 ".css": posts,
55 ".lxt": posts,
56 "fallback": NewUploadImgHandler(dbpool, cfg, st),
57 })
58}
59
60func (h *UploadHandler) GetLogger(s *pssh.SSHServerConnSession) *slog.Logger {
61 logger := pssh.GetLogger(s)
62 if logger == nil {
63 if h.Cfg != nil && h.Cfg.Logger != nil {
64 return h.Cfg.Logger
65 }
66 return slog.Default()
67 }
68 return logger
69}
70
71func (h *UploadHandler) Validate(s *pssh.SSHServerConnSession) error {
72 logger := h.GetLogger(s)
73 user := pssh.GetUser(s)
74
75 if user == nil {
76 err := fmt.Errorf("could not get user from ctx")
77 logger.Error("error getting user from ctx", "err", err)
78 return err
79 }
80
81 ff, err := db.FindAccess(h.DB, user.ID, "prose")
82 if err != nil {
83 return err
84 }
85 ff.SetLimits(h.Cfg.MaxSize, h.Cfg.MaxAssetSize, h.Cfg.MaxSpecialFileSize)
86 setFeatureFlag(s, ff)
87
88 return h.fileRouter.Validate(s)
89}