repos / pico

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

commit
29379e6
parent
97df9d9
author
Eric Bower
date
2025-07-21 10:24:39 -0400 EDT
feat(prose): allow `robots.txt` to be uploaded and served for blog
4 files changed,  +35, -2
M pkg/apps/prose/api.go
+27, -0
 1@@ -328,6 +328,32 @@ func postRawHandler(w http.ResponseWriter, r *http.Request) {
 2 	}
 3 }
 4 
 5+func robotsHandler(w http.ResponseWriter, r *http.Request) {
 6+	username := shared.GetUsernameFromRequest(r)
 7+	cfg := shared.GetCfg(r)
 8+	dbpool := shared.GetDB(r)
 9+	logger := shared.GetLogger(r)
10+	user, err := dbpool.FindUserByName(username)
11+	if err != nil {
12+		logger.Info("blog not found", "user", username)
13+		http.Error(w, "blog not found", http.StatusNotFound)
14+		return
15+	}
16+	logger = shared.LoggerWithUser(logger, user)
17+	w.Header().Add("Content-Type", "text/plain")
18+
19+	post, err := dbpool.FindPostWithFilename("robots.txt", user.ID, cfg.Space)
20+	txt := ""
21+	if err == nil {
22+		txt = post.Text
23+	}
24+	_, err = w.Write([]byte(txt))
25+	if err != nil {
26+		logger.Error("write to response writer", "err", err)
27+		http.Error(w, "server error", 500)
28+	}
29+}
30+
31 func postHandler(w http.ResponseWriter, r *http.Request) {
32 	username := shared.GetUsernameFromRequest(r)
33 	subdomain := shared.GetSubdomain(r)
34@@ -945,6 +971,7 @@ func createSubdomainRoutes(staticRoutes []shared.Route) []shared.Route {
35 	routes := []shared.Route{
36 		shared.NewRoute("GET", "/", blogHandler),
37 		shared.NewRoute("GET", "/_styles.css", blogStyleHandler),
38+		shared.NewRoute("GET", "/robots.txt", robotsHandler),
39 		shared.NewRoute("GET", "/rss", rssBlogHandler),
40 		shared.NewRoute("GET", "/rss.xml", rssBlogHandler),
41 		shared.NewRoute("GET", "/atom.xml", rssBlogHandler),
M pkg/apps/prose/config.go
+1, -1
1@@ -36,7 +36,7 @@ func NewConfigSite(service string) *shared.ConfigSite {
2 			".svg",
3 			".ico",
4 		},
5-		HiddenPosts:  []string{"_readme.md", "_styles.css", "_footer.md", "_404.md"},
6+		HiddenPosts:  []string{"_readme.md", "_styles.css", "_footer.md", "_404.md", "robots.txt"},
7 		Logger:       shared.CreateLogger(service, withPipe),
8 		MaxSize:      maxSize,
9 		MaxAssetSize: maxImgSize,
M pkg/apps/prose/scp_hooks.go
+6, -1
 1@@ -29,10 +29,15 @@ func (p *MarkdownHooks) FileValidate(s *pssh.SSHServerConnSession, data *filehan
 2 		return false, err
 3 	}
 4 
 5+	fp := strings.Replace(data.Filename, "/", "", 1)
 6 	// special styles css file we want to permit but no other css file.
 7 	// sometimes the directory is provided in the filename, so we want to
 8 	// remove that before we perform this check.
 9-	if strings.Replace(data.Filename, "/", "", 1) == "_styles.css" {
10+	if fp == "_styles.css" {
11+		return true, nil
12+	}
13+	// allow users to upload robots file
14+	if fp == "robots.txt" {
15 		return true, nil
16 	}
17 
M pkg/apps/prose/ssh.go
+1, -0
1@@ -53,6 +53,7 @@ func StartSshServer() {
2 
3 	fileMap := map[string]filehandlers.ReadWriteHandler{
4 		".md":      filehandlers.NewScpPostHandler(dbh, cfg, hooks),
5+		".txt":     filehandlers.NewScpPostHandler(dbh, cfg, hooks),
6 		".css":     filehandlers.NewScpPostHandler(dbh, cfg, hooks),
7 		"fallback": uploadimgs.NewUploadImgHandler(dbh, cfg, st),
8 	}