- commit
- eb3b3bb
- parent
- bccde8d
- author
- Eric Bower
- date
- 2026-01-25 09:55:59 -0500 EST
refactor: fix issues with imports and create shared/router pkg
55 files changed,
+479,
-482
+3,
-5
1@@ -13,14 +13,12 @@ import (
2 "github.com/darkweak/souin/pkg/middleware"
3 "github.com/hashicorp/golang-lru/v2/expirable"
4 "github.com/picosh/pico/pkg/apps/pgs"
5- "github.com/picosh/pico/pkg/cache"
6 "github.com/picosh/pico/pkg/shared"
7- "github.com/picosh/utils"
8 "github.com/prometheus/client_golang/prometheus/promhttp"
9 )
10
11 func main() {
12- withPipe := strings.ToLower(utils.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
13+ withPipe := strings.ToLower(shared.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
14 logger := shared.CreateLogger("pgs-cdn", withPipe)
15 ctx := context.Background()
16 drain := pgs.CreateSubCacheDrain(ctx, logger)
17@@ -32,8 +30,8 @@ func main() {
18 httpCache := pgs.SetupCache(cfg)
19 router := &pgs.WebRouter{
20 Cfg: cfg,
21- RedirectsCache: expirable.NewLRU[string, []*pgs.RedirectRule](2048, nil, cache.CacheTimeout),
22- HeadersCache: expirable.NewLRU[string, []*pgs.HeaderRule](2048, nil, cache.CacheTimeout),
23+ RedirectsCache: expirable.NewLRU[string, []*pgs.RedirectRule](2048, nil, shared.CacheTimeout),
24+ HeadersCache: expirable.NewLRU[string, []*pgs.HeaderRule](2048, nil, shared.CacheTimeout),
25 }
26 cacher := &cachedHttp{
27 handler: httpCache,
+2,
-3
1@@ -8,12 +8,11 @@ import (
2 pgsdb "github.com/picosh/pico/pkg/apps/pgs/db"
3 "github.com/picosh/pico/pkg/shared"
4 "github.com/picosh/pico/pkg/shared/storage"
5- "github.com/picosh/utils"
6 "golang.org/x/crypto/ssh"
7 )
8
9 func main() {
10- dbURL := utils.GetEnv("DATABASE_URL", "./data/pgs.sqlite3")
11+ dbURL := shared.GetEnv("DATABASE_URL", "./data/pgs.sqlite3")
12 logger := shared.CreateLogger("pgs-standalone", false)
13 dbpool, err := pgsdb.NewSqliteDB(dbURL, logger)
14 if err != nil {
15@@ -45,7 +44,7 @@ func main() {
16 logger.Error("parse pubkey", "err", err)
17 return
18 }
19- pubkey := utils.KeyForKeyText(key)
20+ pubkey := shared.KeyForKeyText(key)
21 logger.Info("init cli", "userName", userName, "pubkey", pubkey)
22
23 err = dbpool.RegisterAdmin(userName, pubkey, comment)
+2,
-2
1@@ -6,7 +6,7 @@ import (
2
3 "github.com/picosh/pico/pkg/db"
4 "github.com/picosh/pico/pkg/db/postgres"
5- "github.com/picosh/utils"
6+ "github.com/picosh/pico/pkg/shared"
7 )
8
9 func main() {
10@@ -19,7 +19,7 @@ func main() {
11
12 stats, err := dbpool.VisitSummary(
13 &db.SummaryOpts{
14- Origin: utils.StartOfMonth(),
15+ Origin: shared.StartOfMonth(),
16 Host: host,
17 },
18 )
+3,
-3
1@@ -6,7 +6,7 @@ import (
2 "os"
3
4 "github.com/picosh/pico/pkg/db/postgres"
5- "github.com/picosh/pico/pkg/shared"
6+ "github.com/picosh/pico/pkg/shared/router"
7 )
8
9 func main() {
10@@ -47,7 +47,7 @@ func main() {
11
12 update := false
13
14- host, err := shared.CleanHost(origHost)
15+ host, err := router.CleanHost(origHost)
16 if err != nil {
17 fmt.Println(err)
18 }
19@@ -60,7 +60,7 @@ func main() {
20 )
21 }
22
23- ref, err := shared.CleanReferer(origRef)
24+ ref, err := router.CleanReferer(origRef)
25 if err != nil {
26 fmt.Println(err)
27 }
+1,
-2
1@@ -6,7 +6,6 @@ import (
2
3 "github.com/picosh/pico/pkg/db/postgres"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 func main() {
9@@ -25,7 +24,7 @@ func main() {
10 empty := 0
11 diff := 0
12 for _, post := range posts {
13- nextShasum := utils.Shasum([]byte(post.Text))
14+ nextShasum := shared.Shasum([]byte(post.Text))
15 if post.Shasum == "" {
16 empty += 1
17 } else if post.Shasum != nextShasum {
+28,
-28
1@@ -19,7 +19,7 @@ import (
2 "github.com/picosh/pico/pkg/db"
3 "github.com/picosh/pico/pkg/db/postgres"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6+ "github.com/picosh/pico/pkg/shared/router"
7 "github.com/picosh/utils/pipe"
8 "github.com/picosh/utils/pipe/metrics"
9 "github.com/prometheus/client_golang/prometheus/promhttp"
10@@ -48,7 +48,7 @@ func generateURL(cfg *shared.ConfigSite, path string, space string) string {
11 return fmt.Sprintf("%s/%s%s", cfg.Domain, path, query)
12 }
13
14-func wellKnownHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
15+func wellKnownHandler(apiConfig *router.ApiConfig) http.HandlerFunc {
16 return func(w http.ResponseWriter, r *http.Request) {
17 space := r.PathValue("space")
18 if space == "" {
19@@ -80,7 +80,7 @@ type oauth2Introspection struct {
20 Username string `json:"username"`
21 }
22
23-func introspectHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
24+func introspectHandler(apiConfig *router.ApiConfig) http.HandlerFunc {
25 return func(w http.ResponseWriter, r *http.Request) {
26 token := r.FormValue("token")
27 apiConfig.Cfg.Logger.Info("introspect token", "token", token)
28@@ -114,7 +114,7 @@ func introspectHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
29 }
30 }
31
32-func authorizeHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
33+func authorizeHandler(apiConfig *router.ApiConfig) http.HandlerFunc {
34 return func(w http.ResponseWriter, r *http.Request) {
35 responseType := r.URL.Query().Get("response_type")
36 clientID := r.URL.Query().Get("client_id")
37@@ -158,7 +158,7 @@ func authorizeHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
38 }
39 }
40
41-func redirectHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
42+func redirectHandler(apiConfig *router.ApiConfig) http.HandlerFunc {
43 return func(w http.ResponseWriter, r *http.Request) {
44 token := r.FormValue("token")
45 redirectURI := r.FormValue("redirect_uri")
46@@ -194,7 +194,7 @@ type oauth2Token struct {
47 AccessToken string `json:"access_token"`
48 }
49
50-func tokenHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
51+func tokenHandler(apiConfig *router.ApiConfig) http.HandlerFunc {
52 return func(w http.ResponseWriter, r *http.Request) {
53 token := r.FormValue("code")
54 redirectURI := r.FormValue("redirect_uri")
55@@ -233,7 +233,7 @@ type sishData struct {
56 RemoteAddress string `json:"remote_addr"`
57 }
58
59-func keyHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
60+func keyHandler(apiConfig *router.ApiConfig) http.HandlerFunc {
61 return func(w http.ResponseWriter, r *http.Request) {
62 var data sishData
63
64@@ -292,7 +292,7 @@ func keyHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
65 log.Error("cannot insert access log", "err", err)
66 }
67
68- if !apiConfig.HasPrivilegedAccess(shared.GetApiToken(r)) {
69+ if !apiConfig.HasPrivilegedAccess(router.GetApiToken(r)) {
70 w.WriteHeader(http.StatusOK)
71 return
72 }
73@@ -307,9 +307,9 @@ func keyHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
74 }
75 }
76
77-func userHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
78+func userHandler(apiConfig *router.ApiConfig) http.HandlerFunc {
79 return func(w http.ResponseWriter, r *http.Request) {
80- if !apiConfig.HasPrivilegedAccess(shared.GetApiToken(r)) {
81+ if !apiConfig.HasPrivilegedAccess(router.GetApiToken(r)) {
82 w.WriteHeader(http.StatusForbidden)
83 return
84 }
85@@ -354,7 +354,7 @@ func userHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
86 }
87 }
88
89-func rssHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
90+func rssHandler(apiConfig *router.ApiConfig) http.HandlerFunc {
91 return func(w http.ResponseWriter, r *http.Request) {
92 apiToken := r.PathValue("token")
93 user, err := apiConfig.Dbpool.FindUserByToken(apiToken)
94@@ -387,7 +387,7 @@ func rssHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
95 }
96 }
97
98-func pubkeysHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
99+func pubkeysHandler(apiConfig *router.ApiConfig) http.HandlerFunc {
100 return func(w http.ResponseWriter, r *http.Request) {
101 userName := r.PathValue("user")
102 user, err := apiConfig.Dbpool.FindUserByName(userName)
103@@ -456,7 +456,7 @@ type OrderEvent struct {
104
105 // Status code must be 200 or else lemonsqueezy will keep retrying
106 // https://docs.lemonsqueezy.com/help/webhooks
107-func paymentWebhookHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
108+func paymentWebhookHandler(apiConfig *router.ApiConfig) http.HandlerFunc {
109 return func(w http.ResponseWriter, r *http.Request) {
110 dbpool := apiConfig.Dbpool
111 logger := apiConfig.Cfg.Logger
112@@ -482,7 +482,7 @@ func paymentWebhookHandler(apiConfig *shared.ApiConfig) http.HandlerFunc {
113 return
114 }
115
116- hash := shared.HmacString(apiConfig.Cfg.SecretWebhook, string(payload))
117+ hash := router.HmacString(apiConfig.Cfg.SecretWebhook, string(payload))
118 sig := r.Header.Get("X-Signature")
119 if !hmac.Equal([]byte(hash), []byte(sig)) {
120 logger.Error("invalid signature X-Signature")
121@@ -678,14 +678,14 @@ func deserializeCaddyAccessLog(dbpool db.DB, access *AccessLog) (*db.AnalyticsVi
122 } else if strings.HasSuffix(host, "prose.sh") {
123 subdomain = strings.TrimSuffix(host, ".prose.sh")
124 } else {
125- subdomain = shared.GetCustomDomain(host, space)
126+ subdomain = router.GetCustomDomain(host, space)
127 }
128
129 subdomain = strings.TrimSuffix(subdomain, ".nue")
130 subdomain = strings.TrimSuffix(subdomain, ".ash")
131
132 // get user and namespace details from subdomain
133- props, err := shared.GetProjectFromSubdomain(subdomain)
134+ props, err := router.GetProjectFromSubdomain(subdomain)
135 if err != nil {
136 return nil, fmt.Errorf("could not get project from subdomain %s: %w", subdomain, err)
137 }
138@@ -772,7 +772,7 @@ func metricDrainSub(ctx context.Context, dbpool db.DB, logger *slog.Logger, secr
139 }
140
141 logger.Info("received visit", "visit", visit)
142- err = shared.AnalyticsVisitFromVisit(visit, dbpool, secret)
143+ err = router.AnalyticsVisitFromVisit(visit, dbpool, secret)
144 if err != nil {
145 logger.Info("could not record analytics visit", "err", err)
146 continue
147@@ -834,7 +834,7 @@ func tunsEventLogDrainSub(ctx context.Context, dbpool db.DB, logger *slog.Logger
148 }
149 }
150
151-func authMux(apiConfig *shared.ApiConfig) *http.ServeMux {
152+func authMux(apiConfig *router.ApiConfig) *http.ServeMux {
153 serverRoot, err := fs.Sub(embedFS, "public")
154 if err != nil {
155 panic(err)
156@@ -866,24 +866,24 @@ func authMux(apiConfig *shared.ApiConfig) *http.ServeMux {
157 mux.HandleFunc("GET /_metrics", promhttp.Handler().ServeHTTP)
158
159 if apiConfig.Cfg.Debug {
160- shared.CreatePProfRoutesMux(mux)
161+ router.CreatePProfRoutesMux(mux)
162 }
163
164 return mux
165 }
166
167 func StartApiServer() {
168- debug := utils.GetEnv("AUTH_DEBUG", "0")
169- withPipe := strings.ToLower(utils.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
170+ debug := shared.GetEnv("AUTH_DEBUG", "0")
171+ withPipe := strings.ToLower(shared.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
172
173 cfg := &shared.ConfigSite{
174- DbURL: utils.GetEnv("DATABASE_URL", ""),
175+ DbURL: shared.GetEnv("DATABASE_URL", ""),
176 Debug: debug == "1",
177- Issuer: utils.GetEnv("AUTH_ISSUER", "pico.sh"),
178- Domain: utils.GetEnv("AUTH_DOMAIN", "http://0.0.0.0:3000"),
179- Port: utils.GetEnv("AUTH_WEB_PORT", "3000"),
180- Secret: utils.GetEnv("PICO_SECRET", ""),
181- SecretWebhook: utils.GetEnv("PICO_SECRET_WEBHOOK", ""),
182+ Issuer: shared.GetEnv("AUTH_ISSUER", "pico.sh"),
183+ Domain: shared.GetEnv("AUTH_DOMAIN", "http://0.0.0.0:3000"),
184+ Port: shared.GetEnv("AUTH_WEB_PORT", "3000"),
185+ Secret: shared.GetEnv("PICO_SECRET", ""),
186+ SecretWebhook: shared.GetEnv("PICO_SECRET_WEBHOOK", ""),
187 }
188
189 if cfg.SecretWebhook == "" {
190@@ -911,7 +911,7 @@ func StartApiServer() {
191 // gather connect/disconnect logs from tuns
192 go tunsEventLogDrainSub(ctx, db, logger, cfg.Secret)
193
194- apiConfig := &shared.ApiConfig{
195+ apiConfig := &router.ApiConfig{
196 Cfg: cfg,
197 Dbpool: db,
198 }
+4,
-3
1@@ -15,6 +15,7 @@ import (
2 "github.com/picosh/pico/pkg/db"
3 "github.com/picosh/pico/pkg/db/stub"
4 "github.com/picosh/pico/pkg/shared"
5+ "github.com/picosh/pico/pkg/shared/router"
6 )
7
8 var testUserID = "user-1"
9@@ -41,7 +42,7 @@ func TestPaymentWebhook(t *testing.T) {
10 }
11 jso, err := json.Marshal(event)
12 bail(err)
13- hash := shared.HmacString(apiConfig.Cfg.SecretWebhook, string(jso))
14+ hash := router.HmacString(apiConfig.Cfg.SecretWebhook, string(jso))
15 body := bytes.NewReader(jso)
16
17 request := httptest.NewRequest("POST", mkpath("/webhook"), body)
18@@ -275,7 +276,7 @@ func mkpath(path string) string {
19 return fmt.Sprintf("https://auth.pico.test%s", path)
20 }
21
22-func setupTest() *shared.ApiConfig {
23+func setupTest() *router.ApiConfig {
24 logger := shared.CreateLogger("auth-test", false)
25 cfg := &shared.ConfigSite{
26 Issuer: "auth.pico.test",
27@@ -286,7 +287,7 @@ func setupTest() *shared.ApiConfig {
28 }
29 cfg.Logger = logger
30 db := NewAuthDb(cfg.Logger)
31- apiConfig := &shared.ApiConfig{
32+ apiConfig := &router.ApiConfig{
33 Cfg: cfg,
34 Dbpool: db,
35 }
+25,
-24
1@@ -8,13 +8,14 @@ import (
2
3 "github.com/picosh/pico/pkg/db/postgres"
4 "github.com/picosh/pico/pkg/shared"
5+ "github.com/picosh/pico/pkg/shared/router"
6 "github.com/prometheus/client_golang/prometheus/promhttp"
7 )
8
9 func keepAliveHandler(w http.ResponseWriter, r *http.Request) {
10- dbpool := shared.GetDB(r)
11- logger := shared.GetLogger(r)
12- postID, _ := url.PathUnescape(shared.GetField(r, 0))
13+ dbpool := router.GetDB(r)
14+ logger := router.GetLogger(r)
15+ postID, _ := url.PathUnescape(router.GetField(r, 0))
16
17 post, err := dbpool.FindPost(postID)
18 if err != nil {
19@@ -60,9 +61,9 @@ func keepAliveHandler(w http.ResponseWriter, r *http.Request) {
20 }
21
22 func unsubHandler(w http.ResponseWriter, r *http.Request) {
23- dbpool := shared.GetDB(r)
24- logger := shared.GetLogger(r)
25- postID, _ := url.PathUnescape(shared.GetField(r, 0))
26+ dbpool := router.GetDB(r)
27+ logger := router.GetLogger(r)
28+ postID, _ := url.PathUnescape(router.GetField(r, 0))
29
30 post, err := dbpool.FindPost(postID)
31 if err != nil {
32@@ -96,12 +97,12 @@ func unsubHandler(w http.ResponseWriter, r *http.Request) {
33 }
34 }
35
36-func createMainRoutes(staticRoutes []shared.Route) []shared.Route {
37- routes := []shared.Route{
38- shared.NewRoute("GET", "/", shared.CreatePageHandler("html/marketing.page.tmpl")),
39- shared.NewRoute("GET", "/keep-alive/(.+)", keepAliveHandler),
40- shared.NewRoute("GET", "/unsub/(.+)", unsubHandler),
41- shared.NewRoute("GET", "/_metrics", promhttp.Handler().ServeHTTP),
42+func createMainRoutes(staticRoutes []router.Route) []router.Route {
43+ routes := []router.Route{
44+ router.NewRoute("GET", "/", router.CreatePageHandler("html/marketing.page.tmpl")),
45+ router.NewRoute("GET", "/keep-alive/(.+)", keepAliveHandler),
46+ router.NewRoute("GET", "/unsub/(.+)", unsubHandler),
47+ router.NewRoute("GET", "/_metrics", promhttp.Handler().ServeHTTP),
48 }
49
50 routes = append(
51@@ -112,15 +113,15 @@ func createMainRoutes(staticRoutes []shared.Route) []shared.Route {
52 return routes
53 }
54
55-func createStaticRoutes() []shared.Route {
56- return []shared.Route{
57- shared.NewRoute("GET", "/main.css", shared.ServeFile("main.css", "text/css")),
58- shared.NewRoute("GET", "/card.png", shared.ServeFile("card.png", "image/png")),
59- shared.NewRoute("GET", "/favicon-16x16.png", shared.ServeFile("favicon-16x16.png", "image/png")),
60- shared.NewRoute("GET", "/favicon-32x32.png", shared.ServeFile("favicon-32x32.png", "image/png")),
61- shared.NewRoute("GET", "/apple-touch-icon.png", shared.ServeFile("apple-touch-icon.png", "image/png")),
62- shared.NewRoute("GET", "/favicon.ico", shared.ServeFile("favicon.ico", "image/x-icon")),
63- shared.NewRoute("GET", "/robots.txt", shared.ServeFile("robots.txt", "text/plain")),
64+func createStaticRoutes() []router.Route {
65+ return []router.Route{
66+ router.NewRoute("GET", "/main.css", router.ServeFile("main.css", "text/css")),
67+ router.NewRoute("GET", "/card.png", router.ServeFile("card.png", "image/png")),
68+ router.NewRoute("GET", "/favicon-16x16.png", router.ServeFile("favicon-16x16.png", "image/png")),
69+ router.NewRoute("GET", "/favicon-32x32.png", router.ServeFile("favicon-32x32.png", "image/png")),
70+ router.NewRoute("GET", "/apple-touch-icon.png", router.ServeFile("apple-touch-icon.png", "image/png")),
71+ router.NewRoute("GET", "/favicon.ico", router.ServeFile("favicon.ico", "image/x-icon")),
72+ router.NewRoute("GET", "/robots.txt", router.ServeFile("robots.txt", "text/plain")),
73 }
74 }
75
76@@ -139,16 +140,16 @@ func StartApiServer() {
77 staticRoutes := createStaticRoutes()
78
79 if cfg.Debug {
80- staticRoutes = shared.CreatePProfRoutes(staticRoutes)
81+ staticRoutes = router.CreatePProfRoutes(staticRoutes)
82 }
83
84 mainRoutes := createMainRoutes(staticRoutes)
85
86- apiConfig := &shared.ApiConfig{
87+ apiConfig := &router.ApiConfig{
88 Cfg: cfg,
89 Dbpool: db,
90 }
91- handler := shared.CreateServe(mainRoutes, []shared.Route{}, apiConfig)
92+ handler := router.CreateServe(mainRoutes, []router.Route{}, apiConfig)
93 router := http.HandlerFunc(handler)
94
95 portStr := fmt.Sprintf(":%s", cfg.Port)
+7,
-8
1@@ -4,17 +4,16 @@ import (
2 "strings"
3
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 func NewConfigSite(service string) *shared.ConfigSite {
9- debug := utils.GetEnv("FEEDS_DEBUG", "0")
10- domain := utils.GetEnv("FEEDS_DOMAIN", "feeds.pico.sh")
11- port := utils.GetEnv("FEEDS_WEB_PORT", "3000")
12- protocol := utils.GetEnv("FEEDS_PROTOCOL", "https")
13- dbURL := utils.GetEnv("DATABASE_URL", "")
14- sendgridKey := utils.GetEnv("SENDGRID_API_KEY", "")
15- withPipe := strings.ToLower(utils.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
16+ debug := shared.GetEnv("FEEDS_DEBUG", "0")
17+ domain := shared.GetEnv("FEEDS_DOMAIN", "feeds.pico.sh")
18+ port := shared.GetEnv("FEEDS_WEB_PORT", "3000")
19+ protocol := shared.GetEnv("FEEDS_PROTOCOL", "https")
20+ dbURL := shared.GetEnv("DATABASE_URL", "")
21+ sendgridKey := shared.GetEnv("SENDGRID_API_KEY", "")
22+ withPipe := strings.ToLower(shared.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
23
24 return &shared.ConfigSite{
25 Debug: debug == "1",
+1,
-2
1@@ -21,7 +21,6 @@ import (
2 "github.com/mmcdole/gofeed"
3 "github.com/picosh/pico/pkg/db"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 var ErrNoRecentArticles = errors.New("no recent articles")
9@@ -564,7 +563,7 @@ func (f *Fetcher) FetchAll(logger *slog.Logger, urls []string, inlineContent boo
10 }
11
12 // cap body size to prevent abuse
13- if len(html)+len(text) > 5*utils.MB {
14+ if len(html)+len(text) > 5*shared.MB {
15 feeds.Options.InlineContent = false
16 feeds.SizeWarning = true
17 html, err = f.PrintHtml(feeds)
+2,
-3
1@@ -11,7 +11,6 @@ import (
2 "github.com/picosh/pico/pkg/filehandlers"
3 "github.com/picosh/pico/pkg/pssh"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 type FeedHooks struct {
9@@ -20,7 +19,7 @@ type FeedHooks struct {
10 }
11
12 func (p *FeedHooks) FileValidate(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) (bool, error) {
13- if !utils.IsTextFile(string(data.Text)) {
14+ if !shared.IsTextFile(string(data.Text)) {
15 err := fmt.Errorf(
16 "WARNING: (%s) invalid file must be plain text (utf-8), skipping",
17 data.Filename,
18@@ -28,7 +27,7 @@ func (p *FeedHooks) FileValidate(s *pssh.SSHServerConnSession, data *filehandler
19 return false, err
20 }
21
22- if !utils.IsExtAllowed(data.Filename, p.Cfg.AllowedExt) {
23+ if !shared.IsExtAllowed(data.Filename, p.Cfg.AllowedExt) {
24 extStr := strings.Join(p.Cfg.AllowedExt, ",")
25 err := fmt.Errorf(
26 "WARNING: (%s) invalid file, format must be (%s), skipping",
+3,
-4
1@@ -16,15 +16,14 @@ import (
2 "github.com/picosh/pico/pkg/send/protocols/scp"
3 "github.com/picosh/pico/pkg/send/protocols/sftp"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 func StartSshServer() {
9 appName := "feeds-ssh"
10
11- host := utils.GetEnv("FEEDS_HOST", "0.0.0.0")
12- port := utils.GetEnv("FEEDS_SSH_PORT", "2222")
13- promPort := utils.GetEnv("FEEDS_PROM_PORT", "9222")
14+ host := shared.GetEnv("FEEDS_HOST", "0.0.0.0")
15+ port := shared.GetEnv("FEEDS_SSH_PORT", "2222")
16+ promPort := shared.GetEnv("FEEDS_PROM_PORT", "9222")
17 cfg := NewConfigSite(appName)
18 logger := cfg.Logger
19
+54,
-54
1@@ -11,7 +11,7 @@ import (
2 "github.com/picosh/pico/pkg/db"
3 "github.com/picosh/pico/pkg/db/postgres"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6+ "github.com/picosh/pico/pkg/shared/router"
7 "github.com/prometheus/client_golang/prometheus/promhttp"
8 )
9
10@@ -73,11 +73,11 @@ type HeaderTxt struct {
11 }
12
13 func blogHandler(w http.ResponseWriter, r *http.Request) {
14- username := shared.GetUsernameFromRequest(r)
15- dbpool := shared.GetDB(r)
16- blogger := shared.GetLogger(r)
17+ username := router.GetUsernameFromRequest(r)
18+ dbpool := router.GetDB(r)
19+ blogger := router.GetLogger(r)
20 logger := blogger.With("user", username)
21- cfg := shared.GetCfg(r)
22+ cfg := router.GetCfg(r)
23
24 user, err := dbpool.FindUserByName(username)
25 if err != nil {
26@@ -96,7 +96,7 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
27
28 posts := pager.Data
29
30- ts, err := shared.RenderTemplate(cfg, []string{
31+ ts, err := router.RenderTemplate(cfg, []string{
32 cfg.StaticPath("html/blog.page.tmpl"),
33 })
34
35@@ -120,7 +120,7 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
36 Title: post.Filename,
37 PublishAt: post.PublishAt.Format(time.DateOnly),
38 PublishAtISO: post.PublishAt.Format(time.RFC3339),
39- UpdatedTimeAgo: utils.TimeAgo(post.UpdatedAt),
40+ UpdatedTimeAgo: shared.TimeAgo(post.UpdatedAt),
41 UpdatedAtISO: post.UpdatedAt.Format(time.RFC3339),
42 }
43 postCollection = append(postCollection, p)
44@@ -156,19 +156,19 @@ func GetBlogName(username string) string {
45 }
46
47 func postHandler(w http.ResponseWriter, r *http.Request) {
48- username := shared.GetUsernameFromRequest(r)
49- subdomain := shared.GetSubdomain(r)
50- cfg := shared.GetCfg(r)
51+ username := router.GetUsernameFromRequest(r)
52+ subdomain := router.GetSubdomain(r)
53+ cfg := router.GetCfg(r)
54
55 var slug string
56 if !cfg.IsSubdomains() || subdomain == "" {
57- slug, _ = url.PathUnescape(shared.GetField(r, 1))
58+ slug, _ = url.PathUnescape(router.GetField(r, 1))
59 } else {
60- slug, _ = url.PathUnescape(shared.GetField(r, 0))
61+ slug, _ = url.PathUnescape(router.GetField(r, 0))
62 }
63
64- dbpool := shared.GetDB(r)
65- blogger := shared.GetLogger(r)
66+ dbpool := router.GetDB(r)
67+ blogger := router.GetLogger(r)
68 logger := blogger.With("slug", slug, "user", username)
69
70 user, err := dbpool.FindUserByName(username)
71@@ -190,7 +190,7 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
72 unlisted := false
73 parsedText := ""
74 // we dont want to syntax highlight huge files
75- if post.FileSize > 1*utils.MB {
76+ if post.FileSize > 1*shared.MB {
77 logger.Warn("paste too large to parse and apply syntax highlighting")
78 parsedText = post.Text
79 } else {
80@@ -242,7 +242,7 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
81 }
82 }
83
84- ts, err := shared.RenderTemplate(cfg, []string{
85+ ts, err := router.RenderTemplate(cfg, []string{
86 cfg.StaticPath("html/post.page.tmpl"),
87 })
88
89@@ -259,19 +259,19 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
90 }
91
92 func postHandlerRaw(w http.ResponseWriter, r *http.Request) {
93- username := shared.GetUsernameFromRequest(r)
94- subdomain := shared.GetSubdomain(r)
95- cfg := shared.GetCfg(r)
96+ username := router.GetUsernameFromRequest(r)
97+ subdomain := router.GetSubdomain(r)
98+ cfg := router.GetCfg(r)
99
100 var slug string
101 if !cfg.IsSubdomains() || subdomain == "" {
102- slug, _ = url.PathUnescape(shared.GetField(r, 1))
103+ slug, _ = url.PathUnescape(router.GetField(r, 1))
104 } else {
105- slug, _ = url.PathUnescape(shared.GetField(r, 0))
106+ slug, _ = url.PathUnescape(router.GetField(r, 0))
107 }
108
109- dbpool := shared.GetDB(r)
110- blogger := shared.GetLogger(r)
111+ dbpool := router.GetDB(r)
112+ blogger := router.GetLogger(r)
113 logger := blogger.With("user", username, "slug", slug)
114
115 user, err := dbpool.FindUserByName(username)
116@@ -300,8 +300,8 @@ func postHandlerRaw(w http.ResponseWriter, r *http.Request) {
117
118 func serveFile(file string, contentType string) http.HandlerFunc {
119 return func(w http.ResponseWriter, r *http.Request) {
120- logger := shared.GetLogger(r)
121- cfg := shared.GetCfg(r)
122+ logger := router.GetLogger(r)
123+ cfg := router.GetCfg(r)
124
125 contents, err := os.ReadFile(cfg.StaticPath(fmt.Sprintf("public/%s", file)))
126 if err != nil {
127@@ -318,25 +318,25 @@ func serveFile(file string, contentType string) http.HandlerFunc {
128 }
129 }
130
131-func createStaticRoutes() []shared.Route {
132- return []shared.Route{
133- shared.NewRoute("GET", "/main.css", serveFile("main.css", "text/css")),
134- shared.NewRoute("GET", "/smol.css", serveFile("smol.css", "text/css")),
135- shared.NewRoute("GET", "/syntax.css", serveFile("syntax.css", "text/css")),
136- shared.NewRoute("GET", "/card.png", serveFile("card.png", "image/png")),
137- shared.NewRoute("GET", "/favicon-16x16.png", serveFile("favicon-16x16.png", "image/png")),
138- shared.NewRoute("GET", "/favicon-32x32.png", serveFile("favicon-32x32.png", "image/png")),
139- shared.NewRoute("GET", "/apple-touch-icon.png", serveFile("apple-touch-icon.png", "image/png")),
140- shared.NewRoute("GET", "/favicon.ico", serveFile("favicon.ico", "image/x-icon")),
141- shared.NewRoute("GET", "/robots.txt", serveFile("robots.txt", "text/plain")),
142+func createStaticRoutes() []router.Route {
143+ return []router.Route{
144+ router.NewRoute("GET", "/main.css", serveFile("main.css", "text/css")),
145+ router.NewRoute("GET", "/smol.css", serveFile("smol.css", "text/css")),
146+ router.NewRoute("GET", "/syntax.css", serveFile("syntax.css", "text/css")),
147+ router.NewRoute("GET", "/card.png", serveFile("card.png", "image/png")),
148+ router.NewRoute("GET", "/favicon-16x16.png", serveFile("favicon-16x16.png", "image/png")),
149+ router.NewRoute("GET", "/favicon-32x32.png", serveFile("favicon-32x32.png", "image/png")),
150+ router.NewRoute("GET", "/apple-touch-icon.png", serveFile("apple-touch-icon.png", "image/png")),
151+ router.NewRoute("GET", "/favicon.ico", serveFile("favicon.ico", "image/x-icon")),
152+ router.NewRoute("GET", "/robots.txt", serveFile("robots.txt", "text/plain")),
153 }
154 }
155
156-func createMainRoutes(staticRoutes []shared.Route) []shared.Route {
157- routes := []shared.Route{
158- shared.NewRoute("GET", "/", shared.CreatePageHandler("html/marketing.page.tmpl")),
159- shared.NewRoute("GET", "/check", shared.CheckHandler),
160- shared.NewRoute("GET", "/_metrics", promhttp.Handler().ServeHTTP),
161+func createMainRoutes(staticRoutes []router.Route) []router.Route {
162+ routes := []router.Route{
163+ router.NewRoute("GET", "/", router.CreatePageHandler("html/marketing.page.tmpl")),
164+ router.NewRoute("GET", "/check", router.CheckHandler),
165+ router.NewRoute("GET", "/_metrics", promhttp.Handler().ServeHTTP),
166 }
167
168 routes = append(
169@@ -346,18 +346,18 @@ func createMainRoutes(staticRoutes []shared.Route) []shared.Route {
170
171 routes = append(
172 routes,
173- shared.NewRoute("GET", "/([^/]+)", blogHandler),
174- shared.NewRoute("GET", "/([^/]+)/([^/]+)", postHandler),
175- shared.NewRoute("GET", "/([^/]+)/([^/]+)/raw", postHandlerRaw),
176- shared.NewRoute("GET", "/raw/([^/]+)/([^/]+)", postHandlerRaw),
177+ router.NewRoute("GET", "/([^/]+)", blogHandler),
178+ router.NewRoute("GET", "/([^/]+)/([^/]+)", postHandler),
179+ router.NewRoute("GET", "/([^/]+)/([^/]+)/raw", postHandlerRaw),
180+ router.NewRoute("GET", "/raw/([^/]+)/([^/]+)", postHandlerRaw),
181 )
182
183 return routes
184 }
185
186-func createSubdomainRoutes(staticRoutes []shared.Route) []shared.Route {
187- routes := []shared.Route{
188- shared.NewRoute("GET", "/", blogHandler),
189+func createSubdomainRoutes(staticRoutes []router.Route) []router.Route {
190+ routes := []router.Route{
191+ router.NewRoute("GET", "/", blogHandler),
192 }
193
194 routes = append(
195@@ -367,9 +367,9 @@ func createSubdomainRoutes(staticRoutes []shared.Route) []shared.Route {
196
197 routes = append(
198 routes,
199- shared.NewRoute("GET", "/([^/]+)", postHandler),
200- shared.NewRoute("GET", "/([^/]+)/raw", postHandlerRaw),
201- shared.NewRoute("GET", "/raw/([^/]+)", postHandlerRaw),
202+ router.NewRoute("GET", "/([^/]+)", postHandler),
203+ router.NewRoute("GET", "/([^/]+)/raw", postHandlerRaw),
204+ router.NewRoute("GET", "/raw/([^/]+)", postHandlerRaw),
205 )
206
207 return routes
208@@ -388,17 +388,17 @@ func StartApiServer() {
209 staticRoutes := createStaticRoutes()
210
211 if cfg.Debug {
212- staticRoutes = shared.CreatePProfRoutes(staticRoutes)
213+ staticRoutes = router.CreatePProfRoutes(staticRoutes)
214 }
215
216 mainRoutes := createMainRoutes(staticRoutes)
217 subdomainRoutes := createSubdomainRoutes(staticRoutes)
218
219- apiConfig := &shared.ApiConfig{
220+ apiConfig := &router.ApiConfig{
221 Cfg: cfg,
222 Dbpool: db,
223 }
224- handler := shared.CreateServe(mainRoutes, subdomainRoutes, apiConfig)
225+ handler := router.CreateServe(mainRoutes, subdomainRoutes, apiConfig)
226 router := http.HandlerFunc(handler)
227
228 portStr := fmt.Sprintf(":%s", cfg.Port)
+7,
-8
1@@ -4,16 +4,15 @@ import (
2 "strings"
3
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 func NewConfigSite(service string) *shared.ConfigSite {
9- debug := utils.GetEnv("PASTES_DEBUG", "0")
10- domain := utils.GetEnv("PASTES_DOMAIN", "pastes.sh")
11- port := utils.GetEnv("PASTES_WEB_PORT", "3000")
12- dbURL := utils.GetEnv("DATABASE_URL", "")
13- protocol := utils.GetEnv("PASTES_PROTOCOL", "https")
14- withPipe := strings.ToLower(utils.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
15+ debug := shared.GetEnv("PASTES_DEBUG", "0")
16+ domain := shared.GetEnv("PASTES_DOMAIN", "pastes.sh")
17+ port := shared.GetEnv("PASTES_WEB_PORT", "3000")
18+ dbURL := shared.GetEnv("DATABASE_URL", "")
19+ protocol := shared.GetEnv("PASTES_PROTOCOL", "https")
20+ withPipe := strings.ToLower(shared.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
21
22 return &shared.ConfigSite{
23 Debug: debug == "1",
24@@ -23,6 +22,6 @@ func NewConfigSite(service string) *shared.ConfigSite {
25 DbURL: dbURL,
26 Space: "pastes",
27 Logger: shared.CreateLogger(service, withPipe),
28- MaxAssetSize: int64(3 * utils.MB),
29+ MaxAssetSize: int64(3 * shared.MB),
30 }
31 }
+2,
-3
1@@ -11,7 +11,6 @@ import (
2 "github.com/picosh/pico/pkg/filehandlers"
3 "github.com/picosh/pico/pkg/pssh"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 var DEFAULT_EXPIRES_AT = 90
9@@ -22,7 +21,7 @@ type FileHooks struct {
10 }
11
12 func (p *FileHooks) FileValidate(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) (bool, error) {
13- if !utils.IsTextFile(string(data.Text)) {
14+ if !shared.IsTextFile(string(data.Text)) {
15 err := fmt.Errorf(
16 "ERROR: (%s) invalid file must be plain text (utf-8), skipping",
17 data.Filename,
18@@ -43,7 +42,7 @@ func (p *FileHooks) FileValidate(s *pssh.SSHServerConnSession, data *filehandler
19 }
20
21 func (p *FileHooks) FileMeta(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) error {
22- data.Title = utils.ToUpper(data.Slug)
23+ data.Title = shared.ToUpper(data.Slug)
24 // we want the slug to be the filename for pastes
25 data.Slug = data.Filename
26
+3,
-4
1@@ -17,15 +17,14 @@ import (
2 "github.com/picosh/pico/pkg/send/protocols/scp"
3 "github.com/picosh/pico/pkg/send/protocols/sftp"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 func StartSshServer() {
9 appName := "pastes-ssh"
10
11- host := utils.GetEnv("PASTES_HOST", "0.0.0.0")
12- port := utils.GetEnv("PASTES_SSH_PORT", "2222")
13- promPort := utils.GetEnv("PASTES_PROM_PORT", "9222")
14+ host := shared.GetEnv("PASTES_HOST", "0.0.0.0")
15+ port := shared.GetEnv("PASTES_SSH_PORT", "2222")
16+ promPort := shared.GetEnv("PASTES_PROM_PORT", "9222")
17 cfg := NewConfigSite(appName)
18 logger := cfg.Logger
19
+3,
-4
1@@ -14,7 +14,6 @@ import (
2 "github.com/picosh/pico/pkg/db"
3 sst "github.com/picosh/pico/pkg/pobj/storage"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 func NewTabWriter(out io.Writer) *tabwriter.Writer {
9@@ -46,7 +45,7 @@ func projectTable(sesh io.Writer, projects []*db.Project) {
10
11 type Cmd struct {
12 User *db.User
13- Session utils.CmdSession
14+ Session shared.CmdSession
15 Log *slog.Logger
16 Store sst.ObjectStorage
17 Dbpool pgsdb.PgsDB
18@@ -222,8 +221,8 @@ func (c *Cmd) stats(cfgMaxSize uint64) error {
19 _, _ = fmt.Fprintf(
20 writer,
21 "%.4f\t%.4f\t%.4f\t%d\r\n",
22- utils.BytesToGB(int(totalFileSize)),
23- utils.BytesToGB(int(storageMax)),
24+ shared.BytesToGB(int(totalFileSize)),
25+ shared.BytesToGB(int(storageMax)),
26 (float32(totalFileSize)/float32(storageMax))*100,
27 len(projects),
28 )
+11,
-11
1@@ -7,8 +7,8 @@ import (
2 "time"
3
4 pgsdb "github.com/picosh/pico/pkg/apps/pgs/db"
5+ "github.com/picosh/pico/pkg/shared"
6 "github.com/picosh/pico/pkg/shared/storage"
7- "github.com/picosh/utils"
8 )
9
10 type PgsConfig struct {
11@@ -61,26 +61,26 @@ func (c *PgsConfig) StaticPath(fname string) string {
12 return filepath.Join("pkg", "apps", "pgs", fname)
13 }
14
15-var maxSize = uint64(25 * utils.MB)
16-var maxAssetSize = int64(10 * utils.MB)
17+var maxSize = uint64(25 * shared.MB)
18+var maxAssetSize = int64(10 * shared.MB)
19
20 // Needs to be small for caching files like _headers and _redirects.
21-var maxSpecialFileSize = int64(5 * utils.KB)
22+var maxSpecialFileSize = int64(5 * shared.KB)
23
24 func NewPgsConfig(logger *slog.Logger, dbpool pgsdb.PgsDB, st storage.StorageServe, pubsub PicoPubsub) *PgsConfig {
25- domain := utils.GetEnv("PGS_DOMAIN", "pgs.sh")
26- port := utils.GetEnv("PGS_WEB_PORT", "3000")
27- protocol := utils.GetEnv("PGS_PROTOCOL", "https")
28- cacheTTL, err := time.ParseDuration(utils.GetEnv("PGS_CACHE_TTL", ""))
29+ domain := shared.GetEnv("PGS_DOMAIN", "pgs.sh")
30+ port := shared.GetEnv("PGS_WEB_PORT", "3000")
31+ protocol := shared.GetEnv("PGS_PROTOCOL", "https")
32+ cacheTTL, err := time.ParseDuration(shared.GetEnv("PGS_CACHE_TTL", ""))
33 if err != nil {
34 cacheTTL = 600 * time.Second
35 }
36- cacheControl := utils.GetEnv(
37+ cacheControl := shared.GetEnv(
38 "PGS_CACHE_CONTROL",
39 fmt.Sprintf("max-age=%d", int(cacheTTL.Seconds())))
40
41- sshHost := utils.GetEnv("PGS_SSH_HOST", "0.0.0.0")
42- sshPort := utils.GetEnv("PGS_SSH_PORT", "2222")
43+ sshHost := shared.GetEnv("PGS_SSH_HOST", "0.0.0.0")
44+ sshPort := shared.GetEnv("PGS_SSH_PORT", "2222")
45
46 cfg := PgsConfig{
47 CacheControl: cacheControl,
+4,
-4
1@@ -7,7 +7,7 @@ import (
2
3 "github.com/google/uuid"
4 "github.com/picosh/pico/pkg/db"
5- "github.com/picosh/utils"
6+ "github.com/picosh/pico/pkg/shared"
7 )
8
9 type MemoryDB struct {
10@@ -37,9 +37,9 @@ func (me *MemoryDB) SetupTestData() {
11 feature := db.NewFeatureFlag(
12 user.ID,
13 "plus",
14- uint64(25*utils.MB),
15- int64(10*utils.MB),
16- int64(5*utils.KB),
17+ uint64(25*shared.MB),
18+ int64(10*shared.MB),
19+ int64(5*shared.KB),
20 )
21 expiresAt := time.Now().Add(time.Hour * 24)
22 feature.ExpiresAt = &expiresAt
+2,
-2
1@@ -8,7 +8,7 @@ import (
2 "github.com/jmoiron/sqlx"
3 _ "github.com/lib/pq"
4 "github.com/picosh/pico/pkg/db"
5- "github.com/picosh/utils"
6+ "github.com/picosh/pico/pkg/shared"
7 )
8
9 type PgsPsqlDB struct {
10@@ -107,7 +107,7 @@ func (me *PgsPsqlDB) InsertAccessLog(log *db.AccessLog) error {
11 }
12
13 func (me *PgsPsqlDB) InsertProject(userID, name, projectDir string) (string, error) {
14- if !utils.IsValidSubdomain(name) {
15+ if !shared.IsValidSubdomain(name) {
16 return "", fmt.Errorf("'%s' is not a valid project name, must match /^[a-z0-9-]+$/", name)
17 }
18
+3,
-4
1@@ -15,13 +15,12 @@ import (
2 "github.com/picosh/pico/pkg/send/protocols/sftp"
3 "github.com/picosh/pico/pkg/shared"
4 "github.com/picosh/pico/pkg/tunkit"
5- "github.com/picosh/utils"
6 )
7
8 func StartSshServer(cfg *PgsConfig, killCh chan error) {
9- host := utils.GetEnv("PGS_HOST", "0.0.0.0")
10- port := utils.GetEnv("PGS_SSH_PORT", "2222")
11- promPort := utils.GetEnv("PGS_PROM_PORT", "9222")
12+ host := shared.GetEnv("PGS_HOST", "0.0.0.0")
13+ port := shared.GetEnv("PGS_SSH_PORT", "2222")
14+ promPort := shared.GetEnv("PGS_PROM_PORT", "9222")
15 logger := cfg.Logger
16
17 ctx, cancel := context.WithCancel(context.Background())
+3,
-3
1@@ -16,8 +16,8 @@ import (
2
3 pgsdb "github.com/picosh/pico/pkg/apps/pgs/db"
4 "github.com/picosh/pico/pkg/db"
5+ "github.com/picosh/pico/pkg/shared"
6 "github.com/picosh/pico/pkg/shared/storage"
7- "github.com/picosh/utils"
8 "github.com/pkg/sftp"
9 "github.com/prometheus/client_golang/prometheus"
10 "golang.org/x/crypto/ssh"
11@@ -55,7 +55,7 @@ func TestSshServerSftp(t *testing.T) {
12 dbpool.Pubkeys = append(dbpool.Pubkeys, &db.PublicKey{
13 ID: "nice-pubkey",
14 UserID: dbpool.Users[0].ID,
15- Key: utils.KeyForKeyText(user.signer.PublicKey()),
16+ Key: shared.KeyForKeyText(user.signer.PublicKey()),
17 })
18
19 client, err := user.NewClient()
20@@ -139,7 +139,7 @@ func TestSshServerRsync(t *testing.T) {
21 time.Sleep(time.Millisecond * 100)
22
23 user := GenerateUser()
24- key := utils.KeyForKeyText(user.signer.PublicKey())
25+ key := shared.KeyForKeyText(user.signer.PublicKey())
26 // add user's pubkey to the default test account
27 dbpool.Pubkeys = append(dbpool.Pubkeys, &db.PublicKey{
28 ID: "nice-pubkey",
+10,
-10
1@@ -8,7 +8,7 @@ import (
2
3 "github.com/picosh/pico/pkg/db"
4 "github.com/picosh/pico/pkg/pssh"
5- "github.com/picosh/pico/pkg/shared"
6+ "github.com/picosh/pico/pkg/shared/router"
7 "golang.org/x/crypto/ssh"
8 )
9
10@@ -30,7 +30,7 @@ func tunnelPerm(proj *db.Project) bool {
11
12 func (web *TunnelWebRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
13 ctx := r.Context()
14- ctx = context.WithValue(ctx, shared.CtxSubdomainKey{}, web.subdomain)
15+ ctx = context.WithValue(ctx, router.CtxSubdomainKey{}, web.subdomain)
16 web.UserRouter.ServeHTTP(w, r.WithContext(ctx))
17 }
18
19@@ -57,17 +57,17 @@ func CreateHttpHandler(cfg *PgsConfig) CtxHttpBridge {
20 pubkey := ctx.Permissions().Extensions["pubkey"]
21 if pubkey == "" {
22 log.Error("pubkey not found in extensions", "subdomain", subdomain)
23- return http.HandlerFunc(shared.UnauthorizedHandler)
24+ return http.HandlerFunc(router.UnauthorizedHandler)
25 }
26
27 log = log.With(
28 "pubkey", pubkey,
29 )
30
31- props, err := shared.GetProjectFromSubdomain(subdomain)
32+ props, err := router.GetProjectFromSubdomain(subdomain)
33 if err != nil {
34 log.Error("could not get project from subdomain", "err", err.Error())
35- return http.HandlerFunc(shared.UnauthorizedHandler)
36+ return http.HandlerFunc(router.UnauthorizedHandler)
37 }
38
39 owner, err := cfg.DB.FindUserByName(props.Username)
40@@ -77,7 +77,7 @@ func CreateHttpHandler(cfg *PgsConfig) CtxHttpBridge {
41 "name", props.Username,
42 "err", err.Error(),
43 )
44- return http.HandlerFunc(shared.UnauthorizedHandler)
45+ return http.HandlerFunc(router.UnauthorizedHandler)
46 }
47 log = log.With(
48 "owner", owner.Name,
49@@ -86,7 +86,7 @@ func CreateHttpHandler(cfg *PgsConfig) CtxHttpBridge {
50 project, err := cfg.DB.FindProjectByName(owner.ID, props.ProjectName)
51 if err != nil {
52 log.Error("could not get project by name", "project", props.ProjectName, "err", err.Error())
53- return http.HandlerFunc(shared.UnauthorizedHandler)
54+ return http.HandlerFunc(router.UnauthorizedHandler)
55 }
56
57 requester, _ := cfg.DB.FindUserByPubkey(pubkey)
58@@ -108,7 +108,7 @@ func CreateHttpHandler(cfg *PgsConfig) CtxHttpBridge {
59
60 if !isAdmin {
61 log.Error("impersonation attempt failed")
62- return http.HandlerFunc(shared.UnauthorizedHandler)
63+ return http.HandlerFunc(router.UnauthorizedHandler)
64 }
65 requester, _ = cfg.DB.FindUserByName(asUser)
66 }
67@@ -117,11 +117,11 @@ func CreateHttpHandler(cfg *PgsConfig) CtxHttpBridge {
68 publicKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubkey))
69 if err != nil {
70 log.Error("could not parse public key", "pubkey", pubkey, "err", err)
71- return http.HandlerFunc(shared.UnauthorizedHandler)
72+ return http.HandlerFunc(router.UnauthorizedHandler)
73 }
74 if !HasProjectAccess(project, owner, requester, publicKey) {
75 log.Error("no access")
76- return http.HandlerFunc(shared.UnauthorizedHandler)
77+ return http.HandlerFunc(router.UnauthorizedHandler)
78 }
79
80 log.Info("user has access to site")
+7,
-8
1@@ -22,7 +22,6 @@ import (
2 "github.com/picosh/pico/pkg/pssh"
3 sendutils "github.com/picosh/pico/pkg/send/utils"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 ignore "github.com/sabhiram/go-gitignore"
7 )
8
9@@ -405,7 +404,7 @@ func (h *UploadAssetHandler) Write(s *pssh.SSHServerConnSession, entry *sendutil
10
11 fsize, err := h.writeAsset(
12 s,
13- utils.NewMaxBytesReader(data.Reader, int64(sizeRemaining)),
14+ shared.NewMaxBytesReader(data.Reader, int64(sizeRemaining)),
15 data,
16 )
17 if err != nil {
18@@ -413,10 +412,10 @@ func (h *UploadAssetHandler) Write(s *pssh.SSHServerConnSession, entry *sendutil
19 cerr := fmt.Errorf(
20 "%s: storage size %.2fmb, storage max %.2fmb, file max %.2fmb, special file max %.4fmb",
21 err,
22- utils.BytesToMB(int(curStorageSize)),
23- utils.BytesToMB(int(storageMax)),
24- utils.BytesToMB(int(fileMax)),
25- utils.BytesToMB(int(specialFileMax)),
26+ shared.BytesToMB(int(curStorageSize)),
27+ shared.BytesToMB(int(storageMax)),
28+ shared.BytesToMB(int(fileMax)),
29+ shared.BytesToMB(int(specialFileMax)),
30 )
31 return "", cerr
32 }
33@@ -434,8 +433,8 @@ func (h *UploadAssetHandler) Write(s *pssh.SSHServerConnSession, entry *sendutil
34 str := fmt.Sprintf(
35 "%s (space: %.2f/%.2fGB, %.2f%%)",
36 url,
37- utils.BytesToGB(int(nextStorageSize)),
38- utils.BytesToGB(maxSize),
39+ shared.BytesToGB(int(nextStorageSize)),
40+ shared.BytesToGB(maxSize),
41 (float32(nextStorageSize)/float32(maxSize))*100,
42 )
43
+13,
-13
1@@ -22,10 +22,10 @@ import (
2 "github.com/darkweak/storages/core"
3 "github.com/gorilla/feeds"
4 "github.com/hashicorp/golang-lru/v2/expirable"
5- "github.com/picosh/pico/pkg/cache"
6 "github.com/picosh/pico/pkg/db"
7 sst "github.com/picosh/pico/pkg/pobj/storage"
8 "github.com/picosh/pico/pkg/shared"
9+ "github.com/picosh/pico/pkg/shared/router"
10 "github.com/picosh/pico/pkg/shared/storage"
11 "github.com/prometheus/client_golang/prometheus/promhttp"
12 "google.golang.org/protobuf/proto"
13@@ -115,8 +115,8 @@ func NewWebRouter(cfg *PgsConfig) *WebRouter {
14 func newWebRouter(cfg *PgsConfig) *WebRouter {
15 router := &WebRouter{
16 Cfg: cfg,
17- RedirectsCache: expirable.NewLRU[string, []*RedirectRule](2048, nil, cache.CacheTimeout),
18- HeadersCache: expirable.NewLRU[string, []*HeaderRule](2048, nil, cache.CacheTimeout),
19+ RedirectsCache: expirable.NewLRU[string, []*RedirectRule](2048, nil, shared.CacheTimeout),
20+ HeadersCache: expirable.NewLRU[string, []*HeaderRule](2048, nil, shared.CacheTimeout),
21 }
22 router.initRouters()
23 return router
24@@ -242,8 +242,8 @@ func (web *WebRouter) checkHandler(w http.ResponseWriter, r *http.Request) {
25 appDomain := strings.Split(cfg.Domain, ":")[0]
26
27 if !strings.Contains(hostDomain, appDomain) {
28- subdomain := shared.GetCustomDomain(hostDomain, cfg.TxtPrefix)
29- props, err := shared.GetProjectFromSubdomain(subdomain)
30+ subdomain := router.GetCustomDomain(hostDomain, cfg.TxtPrefix)
31+ props, err := router.GetProjectFromSubdomain(subdomain)
32 if err != nil {
33 logger.Error(
34 "could not get project from subdomain",
35@@ -448,7 +448,7 @@ func (web *WebRouter) ImageRequest(perm func(proj *db.Project) bool) http.Handle
36 }
37
38 func (web *WebRouter) ServeAsset(fname string, opts *storage.ImgProcessOpts, hasPerm HasPerm, w http.ResponseWriter, r *http.Request) {
39- subdomain := shared.GetSubdomain(r)
40+ subdomain := router.GetSubdomain(r)
41
42 logger := web.Cfg.Logger.With(
43 "subdomain", subdomain,
44@@ -457,7 +457,7 @@ func (web *WebRouter) ServeAsset(fname string, opts *storage.ImgProcessOpts, has
45 "host", r.Host,
46 )
47
48- props, err := shared.GetProjectFromSubdomain(subdomain)
49+ props, err := router.GetProjectFromSubdomain(subdomain)
50 if err != nil {
51 logger.Info(
52 "could not determine project from subdomain",
53@@ -542,23 +542,23 @@ func (web *WebRouter) ServeAsset(fname string, opts *storage.ImgProcessOpts, has
54 }
55
56 func (web *WebRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
57- subdomain := shared.GetSubdomainFromRequest(r, web.Cfg.Domain, web.Cfg.TxtPrefix)
58+ subdomain := router.GetSubdomainFromRequest(r, web.Cfg.Domain, web.Cfg.TxtPrefix)
59 if web.RootRouter == nil || web.UserRouter == nil {
60 web.Cfg.Logger.Error("routers not initialized")
61 http.Error(w, "routers not initialized", http.StatusInternalServerError)
62 return
63 }
64
65- var router *http.ServeMux
66+ var mux *http.ServeMux
67 if subdomain == "" {
68- router = web.RootRouter
69+ mux = web.RootRouter
70 } else {
71- router = web.UserRouter
72+ mux = web.UserRouter
73 }
74
75 ctx := r.Context()
76- ctx = context.WithValue(ctx, shared.CtxSubdomainKey{}, subdomain)
77- router.ServeHTTP(w, r.WithContext(ctx))
78+ ctx = context.WithValue(ctx, router.CtxSubdomainKey{}, subdomain)
79+ mux.ServeHTTP(w, r.WithContext(ctx))
80 }
81
82 type CompatLogger struct {
+7,
-8
1@@ -12,7 +12,6 @@ import (
2 "github.com/picosh/pico/pkg/db"
3 "github.com/picosh/pico/pkg/pssh"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6
7 pipeLogger "github.com/picosh/utils/pipe/log"
8 )
9@@ -22,7 +21,7 @@ func getUser(s *pssh.SSHServerConnSession, dbpool db.DB) (*db.User, error) {
10 return nil, fmt.Errorf("key not found")
11 }
12
13- key := utils.KeyForKeyText(s.PublicKey())
14+ key := shared.KeyForKeyText(s.PublicKey())
15
16 user, err := dbpool.FindUserByKey(s.User(), key)
17 if err != nil {
18@@ -39,7 +38,7 @@ func getUser(s *pssh.SSHServerConnSession, dbpool db.DB) (*db.User, error) {
19 type Cmd struct {
20 User *db.User
21 SshSession *pssh.SSHServerConnSession
22- Session utils.CmdSession
23+ Session shared.CmdSession
24 Log *slog.Logger
25 Dbpool db.DB
26 Write bool
27@@ -77,9 +76,9 @@ func (c *Cmd) user() {
28 }
29
30 func (c *Cmd) notFound(host, interval string) error {
31- origin := utils.StartOfYear()
32+ origin := shared.StartOfYear()
33 if interval == "month" {
34- origin = utils.StartOfMonth()
35+ origin = shared.StartOfMonth()
36 }
37 c.output(fmt.Sprintf("starting from: %s\n", origin.Format(time.RFC3339)))
38 urls, err := c.Dbpool.VisitUrlNotFound(&db.SummaryOpts{
39@@ -155,10 +154,10 @@ func (c *Cmd) logs(ctx context.Context) error {
40 continue
41 }
42
43- user := utils.AnyToStr(parsedData, "user")
44- userId := utils.AnyToStr(parsedData, "userId")
45+ user := shared.AnyToStr(parsedData, "user")
46+ userId := shared.AnyToStr(parsedData, "userId")
47
48- hidden := utils.AnyToBool(parsedData, "hidden")
49+ hidden := shared.AnyToBool(parsedData, "hidden")
50
51 if !hidden && (user == c.User.Name || userId == c.User.ID) {
52 select {
+3,
-4
1@@ -4,13 +4,12 @@ import (
2 "strings"
3
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 func NewConfigSite(service string) *shared.ConfigSite {
9- dbURL := utils.GetEnv("DATABASE_URL", "")
10- tuns := utils.GetEnv("TUNS_CONSOLE_SECRET", "")
11- withPipe := strings.ToLower(utils.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
12+ dbURL := shared.GetEnv("DATABASE_URL", "")
13+ tuns := shared.GetEnv("TUNS_CONSOLE_SECRET", "")
14+ withPipe := strings.ToLower(shared.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
15
16 return &shared.ConfigSite{
17 DbURL: dbURL,
+2,
-3
1@@ -15,7 +15,6 @@ import (
2 "github.com/picosh/pico/pkg/pssh"
3 sendutils "github.com/picosh/pico/pkg/send/utils"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 "golang.org/x/crypto/ssh"
7 )
8
9@@ -242,7 +241,7 @@ func (h *UploadHandler) ProcessAuthorizedKeys(text []byte, logger *slog.Logger,
10 diff := authorizedKeysDiff(s.PublicKey(), curKeys, nextKeys)
11
12 for _, pk := range diff.Add {
13- key := utils.KeyForKeyText(pk.Pk)
14+ key := shared.KeyForKeyText(pk.Pk)
15
16 _, _ = fmt.Fprintf(s.Stderr(), "adding pubkey (%s)\n", key)
17 logger.Info("adding pubkey", "pubkey", key)
18@@ -255,7 +254,7 @@ func (h *UploadHandler) ProcessAuthorizedKeys(text []byte, logger *slog.Logger,
19 }
20
21 for _, pk := range diff.Update {
22- key := utils.KeyForKeyText(pk.Pk)
23+ key := shared.KeyForKeyText(pk.Pk)
24
25 _, _ = fmt.Fprintf(s.Stderr(), "updating pubkey with comment: %s (%s)\n", pk.Comment, key)
26 logger.Info(
+4,
-5
1@@ -19,7 +19,6 @@ import (
2 "github.com/picosh/pico/pkg/send/protocols/sftp"
3 "github.com/picosh/pico/pkg/shared"
4 "github.com/picosh/pico/pkg/tui"
5- "github.com/picosh/utils"
6 "golang.org/x/crypto/ssh"
7 )
8
9@@ -41,9 +40,9 @@ func createTui(shrd *tui.SharedModel) pssh.SSHServerMiddleware {
10 func StartSshServer() {
11 appName := "pico-ssh"
12
13- host := utils.GetEnv("PICO_HOST", "0.0.0.0")
14- port := utils.GetEnv("PICO_SSH_PORT", "2222")
15- promPort := utils.GetEnv("PICO_PROM_PORT", "9222")
16+ host := shared.GetEnv("PICO_HOST", "0.0.0.0")
17+ port := shared.GetEnv("PICO_SSH_PORT", "2222")
18+ promPort := shared.GetEnv("PICO_PROM_PORT", "9222")
19 cfg := NewConfigSite(appName)
20 logger := cfg.Logger
21
22@@ -84,7 +83,7 @@ func StartSshServer() {
23 if perms == nil {
24 perms = &ssh.Permissions{
25 Extensions: map[string]string{
26- "pubkey": utils.KeyForKeyText(key),
27+ "pubkey": shared.KeyForKeyText(key),
28 },
29 }
30 }
+37,
-36
1@@ -20,6 +20,7 @@ import (
2 "github.com/picosh/pico/pkg/db"
3 "github.com/picosh/pico/pkg/db/postgres"
4 "github.com/picosh/pico/pkg/shared"
5+ "github.com/picosh/pico/pkg/shared/router"
6 "github.com/picosh/utils/pipe"
7 "github.com/prometheus/client_golang/prometheus/promhttp"
8 )
9@@ -36,8 +37,8 @@ var (
10
11 func serveFile(file string, contentType string) http.HandlerFunc {
12 return func(w http.ResponseWriter, r *http.Request) {
13- logger := shared.GetLogger(r)
14- cfg := shared.GetCfg(r)
15+ logger := router.GetLogger(r)
16+ cfg := router.GetCfg(r)
17
18 contents, err := os.ReadFile(cfg.StaticPath(fmt.Sprintf("public/%s", file)))
19 if err != nil {
20@@ -54,18 +55,18 @@ func serveFile(file string, contentType string) http.HandlerFunc {
21 }
22 }
23
24-func createStaticRoutes() []shared.Route {
25- return []shared.Route{
26- shared.NewRoute("GET", "/main.css", serveFile("main.css", "text/css")),
27- shared.NewRoute("GET", "/smol.css", serveFile("smol.css", "text/css")),
28- shared.NewRoute("GET", "/syntax.css", serveFile("syntax.css", "text/css")),
29- shared.NewRoute("GET", "/card.png", serveFile("card.png", "image/png")),
30- shared.NewRoute("GET", "/favicon-16x16.png", serveFile("favicon-16x16.png", "image/png")),
31- shared.NewRoute("GET", "/favicon-32x32.png", serveFile("favicon-32x32.png", "image/png")),
32- shared.NewRoute("GET", "/apple-touch-icon.png", serveFile("apple-touch-icon.png", "image/png")),
33- shared.NewRoute("GET", "/favicon.ico", serveFile("favicon.ico", "image/x-icon")),
34- shared.NewRoute("GET", "/robots.txt", serveFile("robots.txt", "text/plain")),
35- shared.NewRoute("GET", "/anim.js", serveFile("anim.js", "text/javascript")),
36+func createStaticRoutes() []router.Route {
37+ return []router.Route{
38+ router.NewRoute("GET", "/main.css", serveFile("main.css", "text/css")),
39+ router.NewRoute("GET", "/smol.css", serveFile("smol.css", "text/css")),
40+ router.NewRoute("GET", "/syntax.css", serveFile("syntax.css", "text/css")),
41+ router.NewRoute("GET", "/card.png", serveFile("card.png", "image/png")),
42+ router.NewRoute("GET", "/favicon-16x16.png", serveFile("favicon-16x16.png", "image/png")),
43+ router.NewRoute("GET", "/favicon-32x32.png", serveFile("favicon-32x32.png", "image/png")),
44+ router.NewRoute("GET", "/apple-touch-icon.png", serveFile("apple-touch-icon.png", "image/png")),
45+ router.NewRoute("GET", "/favicon.ico", serveFile("favicon.ico", "image/x-icon")),
46+ router.NewRoute("GET", "/robots.txt", serveFile("robots.txt", "text/plain")),
47+ router.NewRoute("GET", "/anim.js", serveFile("anim.js", "text/javascript")),
48 }
49 }
50
51@@ -86,10 +87,10 @@ var _ io.Writer = writeFlusher{}
52
53 func handleSub(pubsub bool) http.HandlerFunc {
54 return func(w http.ResponseWriter, r *http.Request) {
55- logger := shared.GetLogger(r)
56+ logger := router.GetLogger(r)
57
58 clientInfo := shared.NewPicoPipeClient()
59- topic, _ := url.PathUnescape(shared.GetField(r, 0))
60+ topic, _ := url.PathUnescape(router.GetField(r, 0))
61
62 topic = cleanRegex.ReplaceAllString(topic, "")
63
64@@ -139,10 +140,10 @@ func handleSub(pubsub bool) http.HandlerFunc {
65
66 func handlePub(pubsub bool) http.HandlerFunc {
67 return func(w http.ResponseWriter, r *http.Request) {
68- logger := shared.GetLogger(r)
69+ logger := router.GetLogger(r)
70
71 clientInfo := shared.NewPicoPipeClient()
72- topic, _ := url.PathUnescape(shared.GetField(r, 0))
73+ topic, _ := url.PathUnescape(router.GetField(r, 0))
74
75 topic = cleanRegex.ReplaceAllString(topic, "")
76
77@@ -283,7 +284,7 @@ func handlePub(pubsub bool) http.HandlerFunc {
78
79 func handlePipe() http.HandlerFunc {
80 return func(w http.ResponseWriter, r *http.Request) {
81- logger := shared.GetLogger(r)
82+ logger := router.GetLogger(r)
83
84 c, err := upgrader.Upgrade(w, r, nil)
85 if err != nil {
86@@ -296,7 +297,7 @@ func handlePipe() http.HandlerFunc {
87 }()
88
89 clientInfo := shared.NewPicoPipeClient()
90- topic, _ := url.PathUnescape(shared.GetField(r, 0))
91+ topic, _ := url.PathUnescape(router.GetField(r, 0))
92
93 topic = cleanRegex.ReplaceAllString(topic, "")
94
95@@ -435,7 +436,7 @@ func handlePipe() http.HandlerFunc {
96
97 func rssHandler(cfg *shared.ConfigSite, dbpool db.DB) http.HandlerFunc {
98 return func(w http.ResponseWriter, r *http.Request) {
99- apiToken, _ := url.PathUnescape(shared.GetField(r, 0))
100+ apiToken, _ := url.PathUnescape(router.GetField(r, 0))
101 user, err := dbpool.FindUserByToken(apiToken)
102 if err != nil {
103 cfg.Logger.Error(
104@@ -470,20 +471,20 @@ func rssHandler(cfg *shared.ConfigSite, dbpool db.DB) http.HandlerFunc {
105 }
106 }
107
108-func createMainRoutes(staticRoutes []shared.Route, cfg *shared.ConfigSite, dbpool db.DB) []shared.Route {
109- routes := []shared.Route{
110- shared.NewRoute("GET", "/", shared.CreatePageHandler("html/marketing.page.tmpl")),
111- shared.NewRoute("GET", "/check", shared.CheckHandler),
112- shared.NewRoute("GET", "/rss/(.+)", rssHandler(cfg, dbpool)),
113- shared.NewRoute("GET", "/_metrics", promhttp.Handler().ServeHTTP),
114+func createMainRoutes(staticRoutes []router.Route, cfg *shared.ConfigSite, dbpool db.DB) []router.Route {
115+ routes := []router.Route{
116+ router.NewRoute("GET", "/", router.CreatePageHandler("html/marketing.page.tmpl")),
117+ router.NewRoute("GET", "/check", router.CheckHandler),
118+ router.NewRoute("GET", "/rss/(.+)", rssHandler(cfg, dbpool)),
119+ router.NewRoute("GET", "/_metrics", promhttp.Handler().ServeHTTP),
120 }
121
122- pipeRoutes := []shared.Route{
123- shared.NewRoute("GET", "/topic/(.+)", handleSub(false)),
124- shared.NewRoute("POST", "/topic/(.+)", handlePub(false)),
125- shared.NewRoute("GET", "/pubsub/(.+)", handleSub(true)),
126- shared.NewRoute("POST", "/pubsub/(.+)", handlePub(true)),
127- shared.NewRoute("GET", "/pipe/(.+)", handlePipe()),
128+ pipeRoutes := []router.Route{
129+ router.NewRoute("GET", "/topic/(.+)", handleSub(false)),
130+ router.NewRoute("POST", "/topic/(.+)", handlePub(false)),
131+ router.NewRoute("GET", "/pubsub/(.+)", handleSub(true)),
132+ router.NewRoute("POST", "/pubsub/(.+)", handlePub(true)),
133+ router.NewRoute("GET", "/pipe/(.+)", handlePipe()),
134 }
135
136 for _, route := range pipeRoutes {
137@@ -510,7 +511,7 @@ func StartApiServer() {
138 staticRoutes := createStaticRoutes()
139
140 if cfg.Debug {
141- staticRoutes = shared.CreatePProfRoutes(staticRoutes)
142+ staticRoutes = router.CreatePProfRoutes(staticRoutes)
143 }
144
145 mainRoutes := createMainRoutes(staticRoutes, cfg, db)
146@@ -541,11 +542,11 @@ func StartApiServer() {
147 }
148 }()
149
150- apiConfig := &shared.ApiConfig{
151+ apiConfig := &router.ApiConfig{
152 Cfg: cfg,
153 Dbpool: db,
154 }
155- handler := shared.CreateServe(mainRoutes, subdomainRoutes, apiConfig)
156+ handler := router.CreateServe(mainRoutes, subdomainRoutes, apiConfig)
157 router := http.HandlerFunc(handler)
158
159 portStr := fmt.Sprintf(":%s", cfg.Port)
+5,
-6
1@@ -4,15 +4,14 @@ import (
2 "strings"
3
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 func NewConfigSite(service string) *shared.ConfigSite {
9- domain := utils.GetEnv("PIPE_DOMAIN", "pipe.pico.sh")
10- port := utils.GetEnv("PIPE_WEB_PORT", "3000")
11- dbURL := utils.GetEnv("DATABASE_URL", "")
12- protocol := utils.GetEnv("PIPE_PROTOCOL", "https")
13- withPipe := strings.ToLower(utils.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
14+ domain := shared.GetEnv("PIPE_DOMAIN", "pipe.pico.sh")
15+ port := shared.GetEnv("PIPE_WEB_PORT", "3000")
16+ dbURL := shared.GetEnv("DATABASE_URL", "")
17+ protocol := shared.GetEnv("PIPE_PROTOCOL", "https")
18+ withPipe := strings.ToLower(shared.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
19
20 return &shared.ConfigSite{
21 Domain: domain,
+5,
-6
1@@ -11,17 +11,16 @@ import (
2 "github.com/picosh/pico/pkg/pssh"
3 "github.com/picosh/pico/pkg/shared"
4 psub "github.com/picosh/pubsub"
5- "github.com/picosh/utils"
6 "golang.org/x/crypto/ssh"
7 )
8
9 func StartSshServer() {
10 appName := "pipe-ssh"
11
12- host := utils.GetEnv("PIPE_HOST", "0.0.0.0")
13- port := utils.GetEnv("PIPE_SSH_PORT", "2222")
14- portOverride := utils.GetEnv("PIPE_SSH_PORT_OVERRIDE", port)
15- promPort := utils.GetEnv("PIPE_PROM_PORT", "9222")
16+ host := shared.GetEnv("PIPE_HOST", "0.0.0.0")
17+ port := shared.GetEnv("PIPE_SSH_PORT", "2222")
18+ portOverride := shared.GetEnv("PIPE_SSH_PORT_OVERRIDE", port)
19+ promPort := shared.GetEnv("PIPE_PROM_PORT", "9222")
20 cfg := NewConfigSite(appName)
21 logger := cfg.Logger
22
23@@ -62,7 +61,7 @@ func StartSshServer() {
24 if perms == nil {
25 perms = &ssh.Permissions{
26 Extensions: map[string]string{
27- "pubkey": utils.KeyForKeyText(key),
28+ "pubkey": shared.KeyForKeyText(key),
29 },
30 }
31 }
+2,
-3
1@@ -18,7 +18,6 @@ import (
2 "github.com/picosh/pico/pkg/pssh"
3 "github.com/picosh/pico/pkg/shared"
4 psub "github.com/picosh/pubsub"
5- "github.com/picosh/utils"
6 "github.com/prometheus/client_golang/prometheus"
7 "golang.org/x/crypto/ssh"
8 )
9@@ -225,7 +224,7 @@ func NewTestSSHServer(t *testing.T) *TestSSHServer {
10 if perms == nil {
11 perms = &ssh.Permissions{
12 Extensions: map[string]string{
13- "pubkey": utils.KeyForKeyText(key),
14+ "pubkey": shared.KeyForKeyText(key),
15 },
16 }
17 }
18@@ -294,7 +293,7 @@ func GenerateUser(username string) UserSSH {
19 }
20
21 func (u UserSSH) PublicKey() string {
22- return utils.KeyForKeyText(u.signer.PublicKey())
23+ return shared.KeyForKeyText(u.signer.PublicKey())
24 }
25
26 func (u UserSSH) NewClient() (*ssh.Client, error) {
+97,
-97
1@@ -19,8 +19,8 @@ import (
2 "github.com/picosh/pico/pkg/db"
3 "github.com/picosh/pico/pkg/db/postgres"
4 "github.com/picosh/pico/pkg/shared"
5+ "github.com/picosh/pico/pkg/shared/router"
6 "github.com/picosh/pico/pkg/shared/storage"
7- "github.com/picosh/utils"
8 "github.com/prometheus/client_golang/prometheus/promhttp"
9 )
10
11@@ -124,10 +124,10 @@ func GetBlogName(username string) string {
12 }
13
14 func blogStyleHandler(w http.ResponseWriter, r *http.Request) {
15- username := shared.GetUsernameFromRequest(r)
16- dbpool := shared.GetDB(r)
17- logger := shared.GetLogger(r)
18- cfg := shared.GetCfg(r)
19+ username := router.GetUsernameFromRequest(r)
20+ dbpool := router.GetDB(r)
21+ logger := router.GetLogger(r)
22+ cfg := router.GetCfg(r)
23
24 user, err := dbpool.FindUserByName(username)
25 if err != nil {
26@@ -154,10 +154,10 @@ func blogStyleHandler(w http.ResponseWriter, r *http.Request) {
27 }
28
29 func blogHandler(w http.ResponseWriter, r *http.Request) {
30- username := shared.GetUsernameFromRequest(r)
31- dbpool := shared.GetDB(r)
32- logger := shared.GetLogger(r)
33- cfg := shared.GetCfg(r)
34+ username := router.GetUsernameFromRequest(r)
35+ dbpool := router.GetDB(r)
36+ logger := router.GetLogger(r)
37+ cfg := router.GetCfg(r)
38
39 user, err := dbpool.FindUserByName(username)
40 if err != nil {
41@@ -184,7 +184,7 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
42 return
43 }
44
45- ts, err := shared.RenderTemplate(cfg, []string{
46+ ts, err := router.RenderTemplate(cfg, []string{
47 cfg.StaticPath("html/blog-default.partial.tmpl"),
48 cfg.StaticPath("html/blog-aside.partial.tmpl"),
49 cfg.StaticPath("html/blog.page.tmpl"),
50@@ -257,10 +257,10 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
51 p := PostItemData{
52 URL: template.URL(cfg.FullPostURL(curl, post.Username, post.Slug)),
53 BlogURL: template.URL(cfg.FullBlogURL(curl, post.Username)),
54- Title: utils.FilenameToTitle(post.Filename, post.Title),
55+ Title: shared.FilenameToTitle(post.Filename, post.Title),
56 PublishAt: post.PublishAt.Format(time.DateOnly),
57 PublishAtISO: post.PublishAt.Format(time.RFC3339),
58- UpdatedTimeAgo: utils.TimeAgo(post.UpdatedAt),
59+ UpdatedTimeAgo: shared.TimeAgo(post.UpdatedAt),
60 UpdatedAtISO: post.UpdatedAt.Format(time.RFC3339),
61 }
62 postCollection = append(postCollection, p)
63@@ -289,20 +289,20 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
64 }
65
66 func postRawHandler(w http.ResponseWriter, r *http.Request) {
67- username := shared.GetUsernameFromRequest(r)
68- subdomain := shared.GetSubdomain(r)
69- cfg := shared.GetCfg(r)
70+ username := router.GetUsernameFromRequest(r)
71+ subdomain := router.GetSubdomain(r)
72+ cfg := router.GetCfg(r)
73
74 var slug string
75 if !cfg.IsSubdomains() || subdomain == "" {
76- slug, _ = url.PathUnescape(shared.GetField(r, 1))
77+ slug, _ = url.PathUnescape(router.GetField(r, 1))
78 } else {
79- slug, _ = url.PathUnescape(shared.GetField(r, 0))
80+ slug, _ = url.PathUnescape(router.GetField(r, 0))
81 }
82 slug = strings.TrimSuffix(slug, "/")
83
84- dbpool := shared.GetDB(r)
85- logger := shared.GetLogger(r)
86+ dbpool := router.GetDB(r)
87+ logger := router.GetLogger(r)
88 logger = logger.With("slug", slug)
89
90 user, err := dbpool.FindUserByName(username)
91@@ -330,10 +330,10 @@ func postRawHandler(w http.ResponseWriter, r *http.Request) {
92 }
93
94 func robotsHandler(w http.ResponseWriter, r *http.Request) {
95- username := shared.GetUsernameFromRequest(r)
96- cfg := shared.GetCfg(r)
97- dbpool := shared.GetDB(r)
98- logger := shared.GetLogger(r)
99+ username := router.GetUsernameFromRequest(r)
100+ cfg := router.GetCfg(r)
101+ dbpool := router.GetDB(r)
102+ logger := router.GetLogger(r)
103 user, err := dbpool.FindUserByName(username)
104 if err != nil {
105 logger.Info("blog not found", "user", username)
106@@ -356,20 +356,20 @@ func robotsHandler(w http.ResponseWriter, r *http.Request) {
107 }
108
109 func postHandler(w http.ResponseWriter, r *http.Request) {
110- username := shared.GetUsernameFromRequest(r)
111- subdomain := shared.GetSubdomain(r)
112- cfg := shared.GetCfg(r)
113+ username := router.GetUsernameFromRequest(r)
114+ subdomain := router.GetSubdomain(r)
115+ cfg := router.GetCfg(r)
116
117 var slug string
118 if !cfg.IsSubdomains() || subdomain == "" {
119- slug, _ = url.PathUnescape(shared.GetField(r, 1))
120+ slug, _ = url.PathUnescape(router.GetField(r, 1))
121 } else {
122- slug, _ = url.PathUnescape(shared.GetField(r, 0))
123+ slug, _ = url.PathUnescape(router.GetField(r, 0))
124 }
125 slug = strings.TrimSuffix(slug, "/")
126
127- dbpool := shared.GetDB(r)
128- logger := shared.GetLogger(r)
129+ dbpool := router.GetDB(r)
130+ logger := router.GetLogger(r)
131
132 user, err := dbpool.FindUserByName(username)
133 if err != nil {
134@@ -473,7 +473,7 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
135 URL: template.URL(cfg.FullPostURL(curl, post.Username, post.Slug)),
136 BlogURL: template.URL(cfg.FullBlogURL(curl, username)),
137 Description: post.Description,
138- Title: utils.FilenameToTitle(post.Filename, post.Title),
139+ Title: shared.FilenameToTitle(post.Filename, post.Title),
140 Slug: post.Slug,
141 PublishAt: post.PublishAt.Format(time.DateOnly),
142 PublishAtISO: post.PublishAt.Format(time.RFC3339),
143@@ -544,7 +544,7 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
144 w.WriteHeader(http.StatusNotFound)
145 }
146
147- ts, err := shared.RenderTemplate(cfg, []string{
148+ ts, err := router.RenderTemplate(cfg, []string{
149 cfg.StaticPath("html/list.partial.tmpl"),
150 cfg.StaticPath("html/post.page.tmpl"),
151 })
152@@ -563,9 +563,9 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
153 }
154
155 func readHandler(w http.ResponseWriter, r *http.Request) {
156- dbpool := shared.GetDB(r)
157- logger := shared.GetLogger(r)
158- cfg := shared.GetCfg(r)
159+ dbpool := router.GetDB(r)
160+ logger := router.GetLogger(r)
161+ cfg := router.GetCfg(r)
162
163 page, _ := strconv.Atoi(r.URL.Query().Get("page"))
164 tag := r.URL.Query().Get("tag")
165@@ -583,7 +583,7 @@ func readHandler(w http.ResponseWriter, r *http.Request) {
166 return
167 }
168
169- ts, err := shared.RenderTemplate(cfg, []string{
170+ ts, err := router.RenderTemplate(cfg, []string{
171 cfg.StaticPath("html/read.page.tmpl"),
172 })
173
174@@ -625,12 +625,12 @@ func readHandler(w http.ResponseWriter, r *http.Request) {
175 item := PostItemData{
176 URL: template.URL(cfg.FullPostURL(curl, post.Username, post.Slug)),
177 BlogURL: template.URL(cfg.FullBlogURL(curl, post.Username)),
178- Title: utils.FilenameToTitle(post.Filename, post.Title),
179+ Title: shared.FilenameToTitle(post.Filename, post.Title),
180 Description: post.Description,
181 Username: post.Username,
182 PublishAt: post.PublishAt.Format(time.DateOnly),
183 PublishAtISO: post.PublishAt.Format(time.RFC3339),
184- UpdatedTimeAgo: utils.TimeAgo(post.UpdatedAt),
185+ UpdatedTimeAgo: shared.TimeAgo(post.UpdatedAt),
186 UpdatedAtISO: post.UpdatedAt.Format(time.RFC3339),
187 }
188 data.Posts = append(data.Posts, item)
189@@ -644,10 +644,10 @@ func readHandler(w http.ResponseWriter, r *http.Request) {
190 }
191
192 func rssBlogHandler(w http.ResponseWriter, r *http.Request) {
193- username := shared.GetUsernameFromRequest(r)
194- dbpool := shared.GetDB(r)
195- logger := shared.GetLogger(r)
196- cfg := shared.GetCfg(r)
197+ username := router.GetUsernameFromRequest(r)
198+ dbpool := router.GetDB(r)
199+ logger := router.GetLogger(r)
200+ cfg := router.GetCfg(r)
201
202 user, err := dbpool.FindUserByName(username)
203 if err != nil {
204@@ -675,7 +675,7 @@ func rssBlogHandler(w http.ResponseWriter, r *http.Request) {
205 return
206 }
207
208- ts, err := template.New("rss.page.tmpl").Funcs(shared.FuncMap).ParseFiles(
209+ ts, err := template.New("rss.page.tmpl").Funcs(router.FuncMap).ParseFiles(
210 cfg.StaticPath("html/list.partial.tmpl"),
211 cfg.StaticPath("html/rss.page.tmpl"),
212 )
213@@ -775,7 +775,7 @@ func rssBlogHandler(w http.ResponseWriter, r *http.Request) {
214
215 item := &feeds.Item{
216 Id: feedId,
217- Title: utils.FilenameToTitle(post.Filename, post.Title),
218+ Title: shared.FilenameToTitle(post.Filename, post.Title),
219 Link: &feeds.Link{Href: realUrl},
220 Content: content,
221 Updated: *post.PublishAt,
222@@ -805,9 +805,9 @@ func rssBlogHandler(w http.ResponseWriter, r *http.Request) {
223 }
224
225 func rssHandler(w http.ResponseWriter, r *http.Request) {
226- dbpool := shared.GetDB(r)
227- logger := shared.GetLogger(r)
228- cfg := shared.GetCfg(r)
229+ dbpool := router.GetDB(r)
230+ logger := router.GetLogger(r)
231+ cfg := router.GetCfg(r)
232
233 pager, err := dbpool.FindPostsByFeed(&db.Pager{Num: 25, Page: 0}, cfg.Space)
234 if err != nil {
235@@ -816,7 +816,7 @@ func rssHandler(w http.ResponseWriter, r *http.Request) {
236 return
237 }
238
239- ts, err := template.New("rss.page.tmpl").Funcs(shared.FuncMap).ParseFiles(
240+ ts, err := template.New("rss.page.tmpl").Funcs(router.FuncMap).ParseFiles(
241 cfg.StaticPath("html/list.partial.tmpl"),
242 cfg.StaticPath("html/rss.page.tmpl"),
243 )
244@@ -907,8 +907,8 @@ func rssHandler(w http.ResponseWriter, r *http.Request) {
245
246 func serveFile(file string, contentType string) http.HandlerFunc {
247 return func(w http.ResponseWriter, r *http.Request) {
248- logger := shared.GetLogger(r)
249- cfg := shared.GetCfg(r)
250+ logger := router.GetLogger(r)
251+ cfg := router.GetCfg(r)
252
253 contents, err := os.ReadFile(cfg.StaticPath(fmt.Sprintf("public/%s", file)))
254 if err != nil {
255@@ -925,29 +925,29 @@ func serveFile(file string, contentType string) http.HandlerFunc {
256 }
257 }
258
259-func createStaticRoutes() []shared.Route {
260- return []shared.Route{
261- shared.NewRoute("GET", "/main.css", serveFile("main.css", "text/css")),
262- shared.NewRoute("GET", "/smol.css", serveFile("smol.css", "text/css")),
263- shared.NewRoute("GET", "/smol-v2.css", serveFile("smol-v2.css", "text/css")),
264- shared.NewRoute("GET", "/syntax.css", serveFile("syntax.css", "text/css")),
265- shared.NewRoute("GET", "/card.png", serveFile("card.png", "image/png")),
266- shared.NewRoute("GET", "/favicon-16x16.png", serveFile("favicon-16x16.png", "image/png")),
267- shared.NewRoute("GET", "/favicon-32x32.png", serveFile("favicon-32x32.png", "image/png")),
268- shared.NewRoute("GET", "/apple-touch-icon.png", serveFile("apple-touch-icon.png", "image/png")),
269- shared.NewRoute("GET", "/favicon.ico", serveFile("favicon.ico", "image/x-icon")),
270- shared.NewRoute("GET", "/robots.txt", serveFile("robots.txt", "text/plain")),
271+func createStaticRoutes() []router.Route {
272+ return []router.Route{
273+ router.NewRoute("GET", "/main.css", serveFile("main.css", "text/css")),
274+ router.NewRoute("GET", "/smol.css", serveFile("smol.css", "text/css")),
275+ router.NewRoute("GET", "/smol-v2.css", serveFile("smol-v2.css", "text/css")),
276+ router.NewRoute("GET", "/syntax.css", serveFile("syntax.css", "text/css")),
277+ router.NewRoute("GET", "/card.png", serveFile("card.png", "image/png")),
278+ router.NewRoute("GET", "/favicon-16x16.png", serveFile("favicon-16x16.png", "image/png")),
279+ router.NewRoute("GET", "/favicon-32x32.png", serveFile("favicon-32x32.png", "image/png")),
280+ router.NewRoute("GET", "/apple-touch-icon.png", serveFile("apple-touch-icon.png", "image/png")),
281+ router.NewRoute("GET", "/favicon.ico", serveFile("favicon.ico", "image/x-icon")),
282+ router.NewRoute("GET", "/robots.txt", serveFile("robots.txt", "text/plain")),
283 }
284 }
285
286-func createMainRoutes(staticRoutes []shared.Route) []shared.Route {
287- routes := []shared.Route{
288- shared.NewRoute("GET", "/", readHandler),
289- shared.NewRoute("GET", "/read", readHandler),
290- shared.NewRoute("GET", "/check", shared.CheckHandler),
291- shared.NewRoute("GET", "/rss", rssHandler),
292- shared.NewRoute("GET", "/rss.atom", rssHandler),
293- shared.NewRoute("GET", "/_metrics", promhttp.Handler().ServeHTTP),
294+func createMainRoutes(staticRoutes []router.Route) []router.Route {
295+ routes := []router.Route{
296+ router.NewRoute("GET", "/", readHandler),
297+ router.NewRoute("GET", "/read", readHandler),
298+ router.NewRoute("GET", "/check", router.CheckHandler),
299+ router.NewRoute("GET", "/rss", rssHandler),
300+ router.NewRoute("GET", "/rss.atom", rssHandler),
301+ router.NewRoute("GET", "/_metrics", promhttp.Handler().ServeHTTP),
302 }
303
304 routes = append(
305@@ -959,10 +959,10 @@ func createMainRoutes(staticRoutes []shared.Route) []shared.Route {
306 }
307
308 func imgRequest(w http.ResponseWriter, r *http.Request) {
309- logger := shared.GetLogger(r)
310- st := shared.GetStorage(r)
311- dbpool := shared.GetDB(r)
312- username := shared.GetUsernameFromRequest(r)
313+ logger := router.GetLogger(r)
314+ st := router.GetStorage(r)
315+ dbpool := router.GetDB(r)
316+ username := router.GetUsernameFromRequest(r)
317 user, err := dbpool.FindUserByName(username)
318 if err != nil {
319 logger.Error("could not find user", "username", username)
320@@ -971,8 +971,8 @@ func imgRequest(w http.ResponseWriter, r *http.Request) {
321 }
322 logger = shared.LoggerWithUser(logger, user)
323
324- rawname := shared.GetField(r, 0)
325- imgOpts := shared.GetField(r, 1)
326+ rawname := router.GetField(r, 0)
327+ imgOpts := router.GetField(r, 1)
328 // we place all prose images inside a "prose" folder
329 fname := filepath.Join("/prose", rawname)
330
331@@ -1037,17 +1037,17 @@ func imgRequest(w http.ResponseWriter, r *http.Request) {
332 }
333 }
334
335-func createSubdomainRoutes(staticRoutes []shared.Route) []shared.Route {
336- routes := []shared.Route{
337- shared.NewRoute("GET", "/", blogHandler),
338- shared.NewRoute("GET", "/_styles.css", blogStyleHandler),
339- shared.NewRoute("GET", "/robots.txt", robotsHandler),
340- shared.NewRoute("GET", "/rss", rssBlogHandler),
341- shared.NewRoute("GET", "/rss.xml", rssBlogHandler),
342- shared.NewRoute("GET", "/atom.xml", rssBlogHandler),
343- shared.NewRoute("GET", "/feed.xml", rssBlogHandler),
344- shared.NewRoute("GET", "/atom", rssBlogHandler),
345- shared.NewRoute("GET", "/blog/index.xml", rssBlogHandler),
346+func createSubdomainRoutes(staticRoutes []router.Route) []router.Route {
347+ routes := []router.Route{
348+ router.NewRoute("GET", "/", blogHandler),
349+ router.NewRoute("GET", "/_styles.css", blogStyleHandler),
350+ router.NewRoute("GET", "/robots.txt", robotsHandler),
351+ router.NewRoute("GET", "/rss", rssBlogHandler),
352+ router.NewRoute("GET", "/rss.xml", rssBlogHandler),
353+ router.NewRoute("GET", "/atom.xml", rssBlogHandler),
354+ router.NewRoute("GET", "/feed.xml", rssBlogHandler),
355+ router.NewRoute("GET", "/atom", rssBlogHandler),
356+ router.NewRoute("GET", "/blog/index.xml", rssBlogHandler),
357 }
358
359 routes = append(
360@@ -1057,13 +1057,13 @@ func createSubdomainRoutes(staticRoutes []shared.Route) []shared.Route {
361
362 routes = append(
363 routes,
364- shared.NewRoute("GET", "/raw/(.+)", postRawHandler),
365- shared.NewRoute("GET", "/(.+).md", postRawHandler),
366- shared.NewRoute("GET", "/(.+).lxt", postRawHandler),
367- shared.NewRoute("GET", `/(.+\.(?:jpg|jpeg|png|gif|webp|svg|ico))/(.+)`, imgRequest),
368- shared.NewRoute("GET", `/(.+\.(?:jpg|jpeg|png|gif|webp|svg|ico))$`, imgRequest),
369- shared.NewRoute("GET", "/(.+).html", postHandler),
370- shared.NewRoute("GET", "/(.+)", postHandler),
371+ router.NewRoute("GET", "/raw/(.+)", postRawHandler),
372+ router.NewRoute("GET", "/(.+).md", postRawHandler),
373+ router.NewRoute("GET", "/(.+).lxt", postRawHandler),
374+ router.NewRoute("GET", `/(.+\.(?:jpg|jpeg|png|gif|webp|svg|ico))/(.+)`, imgRequest),
375+ router.NewRoute("GET", `/(.+\.(?:jpg|jpeg|png|gif|webp|svg|ico))$`, imgRequest),
376+ router.NewRoute("GET", "/(.+).html", postHandler),
377+ router.NewRoute("GET", "/(.+)", postHandler),
378 )
379
380 return routes
381@@ -1087,18 +1087,18 @@ func StartApiServer() {
382 staticRoutes := createStaticRoutes()
383
384 if cfg.Debug {
385- staticRoutes = shared.CreatePProfRoutes(staticRoutes)
386+ staticRoutes = router.CreatePProfRoutes(staticRoutes)
387 }
388
389 mainRoutes := createMainRoutes(staticRoutes)
390 subdomainRoutes := createSubdomainRoutes(staticRoutes)
391
392- apiConfig := &shared.ApiConfig{
393+ apiConfig := &router.ApiConfig{
394 Cfg: cfg,
395 Dbpool: dbpool,
396 Storage: st,
397 }
398- handler := shared.CreateServe(mainRoutes, subdomainRoutes, apiConfig)
399+ handler := router.CreateServe(mainRoutes, subdomainRoutes, apiConfig)
400 router := http.HandlerFunc(handler)
401
402 portStr := fmt.Sprintf(":%s", cfg.Port)
+9,
-10
1@@ -4,20 +4,19 @@ import (
2 "strings"
3
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8-var MAX_FILE_SIZE = 3 * utils.MB
9+var MAX_FILE_SIZE = 3 * shared.MB
10
11 func NewConfigSite(service string) *shared.ConfigSite {
12- debug := utils.GetEnv("PROSE_DEBUG", "0")
13- domain := utils.GetEnv("PROSE_DOMAIN", "prose.sh")
14- port := utils.GetEnv("PROSE_WEB_PORT", "3000")
15- protocol := utils.GetEnv("PROSE_PROTOCOL", "https")
16- dbURL := utils.GetEnv("DATABASE_URL", "")
17- maxSize := uint64(25 * utils.MB)
18- maxImgSize := int64(10 * utils.MB)
19- withPipe := strings.ToLower(utils.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
20+ debug := shared.GetEnv("PROSE_DEBUG", "0")
21+ domain := shared.GetEnv("PROSE_DOMAIN", "prose.sh")
22+ port := shared.GetEnv("PROSE_WEB_PORT", "3000")
23+ protocol := shared.GetEnv("PROSE_PROTOCOL", "https")
24+ dbURL := shared.GetEnv("DATABASE_URL", "")
25+ maxSize := uint64(25 * shared.MB)
26+ maxImgSize := int64(10 * shared.MB)
27+ withPipe := strings.ToLower(shared.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
28
29 return &shared.ConfigSite{
30 Debug: debug == "1",
+4,
-5
1@@ -11,7 +11,6 @@ import (
2 "github.com/picosh/pico/pkg/filehandlers"
3 "github.com/picosh/pico/pkg/pssh"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 pipeUtil "github.com/picosh/utils/pipe"
7 )
8
9@@ -22,7 +21,7 @@ type MarkdownHooks struct {
10 }
11
12 func (p *MarkdownHooks) FileValidate(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) (bool, error) {
13- if !utils.IsTextFile(data.Text) {
14+ if !shared.IsTextFile(data.Text) {
15 err := fmt.Errorf(
16 "ERROR: (%s) invalid file must be plain text (utf-8), skipping",
17 data.Filename,
18@@ -42,7 +41,7 @@ func (p *MarkdownHooks) FileValidate(s *pssh.SSHServerConnSession, data *filehan
19 return true, nil
20 }
21
22- if !utils.IsExtAllowed(data.Filename, p.Cfg.AllowedExt) {
23+ if !shared.IsExtAllowed(data.Filename, p.Cfg.AllowedExt) {
24 extStr := strings.Join(p.Cfg.AllowedExt, ",")
25 err := fmt.Errorf(
26 "ERROR: (%s) invalid file, format must be (%s), skipping",
27@@ -67,7 +66,7 @@ func (p *MarkdownHooks) metaLxt(data *filehandlers.PostMetaData) error {
28 parsedText := shared.ListParseText(data.Text)
29
30 if parsedText.Title == "" {
31- data.Title = utils.ToUpper(data.Slug)
32+ data.Title = shared.ToUpper(data.Slug)
33 } else {
34 data.Title = parsedText.Title
35 }
36@@ -91,7 +90,7 @@ func (p *MarkdownHooks) metaMd(data *filehandlers.PostMetaData) error {
37 }
38
39 if parsedText.Title == "" {
40- data.Title = utils.ToUpper(data.Slug)
41+ data.Title = shared.ToUpper(data.Slug)
42 } else {
43 data.Title = parsedText.Title
44 }
+3,
-4
1@@ -19,15 +19,14 @@ import (
2 "github.com/picosh/pico/pkg/send/protocols/sftp"
3 "github.com/picosh/pico/pkg/shared"
4 "github.com/picosh/pico/pkg/shared/storage"
5- "github.com/picosh/utils"
6 )
7
8 func StartSshServer() {
9 appName := "prose-ssh"
10
11- host := utils.GetEnv("PROSE_HOST", "0.0.0.0")
12- port := utils.GetEnv("PROSE_SSH_PORT", "2222")
13- promPort := utils.GetEnv("PROSE_PROM_PORT", "9222")
14+ host := shared.GetEnv("PROSE_HOST", "0.0.0.0")
15+ port := shared.GetEnv("PROSE_SSH_PORT", "2222")
16+ promPort := shared.GetEnv("PROSE_PROM_PORT", "9222")
17 cfg := NewConfigSite(appName)
18 logger := cfg.Logger
19
+0,
-21
1@@ -1,21 +0,0 @@
2-package cache
3-
4-import (
5- "log/slog"
6- "time"
7-
8- "github.com/picosh/utils"
9-)
10-
11-var CacheTimeout time.Duration
12-
13-func init() {
14- cacheDuration := utils.GetEnv("STORAGE_MINIO_CACHE_DURATION", "1m")
15- duration, err := time.ParseDuration(cacheDuration)
16- if err != nil {
17- slog.Error("Invalid STORAGE_MINIO_CACHE_DURATION value, using default 1m", "error", err)
18- duration = 1 * time.Minute
19- }
20-
21- CacheTimeout = duration
22-}
+2,
-2
1@@ -14,7 +14,7 @@ import (
2 "github.com/jmoiron/sqlx"
3 _ "github.com/lib/pq"
4 "github.com/picosh/pico/pkg/db"
5- "github.com/picosh/utils"
6+ "github.com/picosh/pico/pkg/shared"
7 )
8
9 var PAGER_SIZE = 15
10@@ -1232,7 +1232,7 @@ func (me *PsqlDB) FindFeedItemsByPostID(postID string) ([]*db.FeedItem, error) {
11 }
12
13 func (me *PsqlDB) InsertProject(userID, name, projectDir string) (string, error) {
14- if !utils.IsValidSubdomain(name) {
15+ if !shared.IsValidSubdomain(name) {
16 return "", fmt.Errorf("'%s' is not a valid project name, must match /^[a-z0-9-]+$/", name)
17 }
18
+3,
-4
1@@ -19,7 +19,6 @@ import (
2 sendutils "github.com/picosh/pico/pkg/send/utils"
3 "github.com/picosh/pico/pkg/shared"
4 "github.com/picosh/pico/pkg/shared/storage"
5- "github.com/picosh/utils"
6 )
7
8 var Space = "imgs"
9@@ -214,8 +213,8 @@ func (h *UploadImgHandler) Write(s *pssh.SSHServerConnSession, entry *sendutils.
10 str := fmt.Sprintf(
11 "%s (space: %.2f/%.2fGB, %.2f%%)",
12 url,
13- utils.BytesToGB(metadata.TotalFileSize+fileSize),
14- utils.BytesToGB(maxSize),
15+ shared.BytesToGB(metadata.TotalFileSize+fileSize),
16+ shared.BytesToGB(maxSize),
17 (float32(totalFileSize)/float32(maxSize))*100,
18 )
19 return str, nil
20@@ -262,7 +261,7 @@ func (h *UploadImgHandler) validateImg(data *PostMetaData) (bool, error) {
21 return false, fmt.Errorf("ERROR: user (%s) has exceeded (%d bytes) max (%d bytes)", data.User.Name, data.TotalFileSize, storageMax)
22 }
23
24- if !utils.IsExtAllowed(data.Filename, h.Cfg.AllowedExt) {
25+ if !shared.IsExtAllowed(data.Filename, h.Cfg.AllowedExt) {
26 extStr := strings.Join(h.Cfg.AllowedExt, ",")
27 err := fmt.Errorf(
28 "ERROR: (%s) invalid file, format must be (%s), skipping",
+2,
-3
1@@ -14,7 +14,6 @@ import (
2 "github.com/picosh/pico/pkg/pssh"
3 sendutils "github.com/picosh/pico/pkg/send/utils"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 type PostMetaData struct {
9@@ -116,9 +115,9 @@ func (h *ScpUploadHandler) Write(s *pssh.SSHServerConnSession, entry *sendutils.
10 }
11
12 now := time.Now()
13- slug := utils.SanitizeFileExt(filename)
14+ slug := shared.SanitizeFileExt(filename)
15 fileSize := binary.Size(origText)
16- shasum := utils.Shasum(origText)
17+ shasum := shared.Shasum(origText)
18
19 nextPost := db.Post{
20 Filename: filename,
+4,
-2
1@@ -17,9 +17,11 @@ import (
2 "github.com/google/renameio/v2"
3 "github.com/picosh/pico/pkg/send/utils"
4 "github.com/picosh/pico/pkg/shared/mime"
5- putils "github.com/picosh/utils"
6 )
7
8+var KB = 1000
9+var MB = KB * 1000
10+
11 // https://stackoverflow.com/a/32482941
12 func dirSize(path string) (int64, error) {
13 var size int64
14@@ -121,7 +123,7 @@ func (s *StorageFS) GetObject(bucket Bucket, fpath string) (utils.ReadAndReaderA
15
16 etag := ""
17 // only generate etag if file is less than 10MB
18- if info.Size() <= int64(10*putils.MB) {
19+ if info.Size() <= int64(10*MB) {
20 // calculate etag
21 h := md5.New()
22 if _, err := io.Copy(h, dat); err != nil {
1@@ -0,0 +1,5 @@
2+package shared
3+
4+import "time"
5+
6+var CacheTimeout = 2 * time.Minute
1@@ -0,0 +1,37 @@
2+package shared
3+
4+import (
5+ "fmt"
6+ "io"
7+ "log/slog"
8+ "os"
9+)
10+
11+type CmdSessionLogger struct {
12+ Log *slog.Logger
13+}
14+
15+func (c *CmdSessionLogger) Write(out []byte) (int, error) {
16+ c.Log.Info(string(out))
17+ return 0, nil
18+}
19+
20+func (c *CmdSessionLogger) Exit(code int) error {
21+ os.Exit(code)
22+ return fmt.Errorf("panic %d", code)
23+}
24+
25+func (c *CmdSessionLogger) Close() error {
26+ return fmt.Errorf("closing")
27+}
28+
29+func (c *CmdSessionLogger) Stderr() io.ReadWriter {
30+ return nil
31+}
32+
33+type CmdSession interface {
34+ Write([]byte) (int, error)
35+ Exit(code int) error
36+ Close() error
37+ Stderr() io.ReadWriter
38+}
1@@ -1,17 +1,16 @@
2 package shared
3
4 import (
5- "github.com/picosh/utils"
6 "github.com/picosh/utils/pipe"
7 )
8
9 func NewPicoPipeClient() *pipe.SSHClientInfo {
10 return &pipe.SSHClientInfo{
11- RemoteHost: utils.GetEnv("PICO_PIPE_ENDPOINT", "pipe.pico.sh:22"),
12- KeyLocation: utils.GetEnv("PICO_PIPE_KEY", "ssh_data/term_info_ed25519"),
13- CertificateLocation: utils.GetEnv("PICO_PIPE_KEY_CERT", ""),
14- KeyPassphrase: utils.GetEnv("PICO_PIPE_PASSPHRASE", ""),
15- RemoteHostname: utils.GetEnv("PICO_PIPE_REMOTE_HOST", "pipe.pico.sh"),
16- RemoteUser: utils.GetEnv("PICO_PIPE_USER", "pico"),
17+ RemoteHost: GetEnv("PICO_PIPE_ENDPOINT", "pipe.pico.sh:22"),
18+ KeyLocation: GetEnv("PICO_PIPE_KEY", "ssh_data/term_info_ed25519"),
19+ CertificateLocation: GetEnv("PICO_PIPE_KEY_CERT", ""),
20+ KeyPassphrase: GetEnv("PICO_PIPE_PASSPHRASE", ""),
21+ RemoteHostname: GetEnv("PICO_PIPE_REMOTE_HOST", "pipe.pico.sh"),
22+ RemoteUser: GetEnv("PICO_PIPE_USER", "pico"),
23 }
24 }
1@@ -1,4 +1,4 @@
2-package shared
3+package router
4
5 import (
6 "context"
7@@ -16,6 +16,7 @@ import (
8 "time"
9
10 "github.com/picosh/pico/pkg/db"
11+ "github.com/picosh/pico/pkg/shared"
12 "github.com/picosh/utils/pipe/metrics"
13 "github.com/simplesurance/go-ip-anonymizer/ipanonymizer"
14 "github.com/x-way/crawlerdetect"
15@@ -233,7 +234,7 @@ func AnalyticsCollect(ch chan *db.AnalyticsVisits, dbpool db.DB, logger *slog.Lo
16 drain := metrics.RegisterReconnectMetricRecorder(
17 context.Background(),
18 logger,
19- NewPicoPipeClient(),
20+ shared.NewPicoPipeClient(),
21 100,
22 10*time.Millisecond,
23 )
1@@ -1,4 +1,4 @@
2-package shared
3+package router
4
5 import (
6 "encoding/json"
7@@ -9,7 +9,7 @@ import (
8 "strings"
9
10 "github.com/picosh/pico/pkg/db"
11- "github.com/picosh/utils"
12+ "github.com/picosh/pico/pkg/shared"
13 "golang.org/x/crypto/ssh"
14 )
15
16@@ -61,7 +61,7 @@ type UserApi struct {
17 func NewUserApi(user *db.User, pubkey ssh.PublicKey) *UserApi {
18 return &UserApi{
19 User: user,
20- Fingerprint: utils.KeyForSha256(pubkey),
21+ Fingerprint: shared.KeyForSha256(pubkey),
22 }
23 }
24
25@@ -136,7 +136,7 @@ var FuncMap = template.FuncMap{
26 "intRange": intRange,
27 }
28
29-func RenderTemplate(cfg *ConfigSite, templates []string) (*template.Template, error) {
30+func RenderTemplate(cfg *shared.ConfigSite, templates []string) (*template.Template, error) {
31 files := make([]string, len(templates))
32 copy(files, templates)
33 files = append(
34@@ -165,7 +165,7 @@ func CreatePageHandler(fname string) http.HandlerFunc {
35 return
36 }
37
38- data := PageData{
39+ data := shared.PageData{
40 Site: *cfg.GetSiteData(),
41 }
42 err = ts.Execute(w, data)
1@@ -1,4 +1,4 @@
2-package shared
3+package router
4
5 import (
6 "context"
7@@ -11,9 +11,9 @@ import (
8 "strings"
9
10 "github.com/hashicorp/golang-lru/v2/expirable"
11- "github.com/picosh/pico/pkg/cache"
12 "github.com/picosh/pico/pkg/db"
13 "github.com/picosh/pico/pkg/pssh"
14+ "github.com/picosh/pico/pkg/shared"
15 "github.com/picosh/pico/pkg/shared/storage"
16 )
17
18@@ -71,7 +71,7 @@ func CreatePProfRoutesMux(mux *http.ServeMux) {
19 }
20
21 type ApiConfig struct {
22- Cfg *ConfigSite
23+ Cfg *shared.ConfigSite
24 Dbpool db.DB
25 Storage storage.StorageServe
26 }
27@@ -147,7 +147,7 @@ func GetSubdomainFromRequest(r *http.Request, domain, space string) string {
28 return ""
29 }
30
31-func findRouteConfig(r *http.Request, routes []Route, subdomainRoutes []Route, cfg *ConfigSite) ([]Route, string) {
32+func findRouteConfig(r *http.Request, routes []Route, subdomainRoutes []Route, cfg *shared.ConfigSite) ([]Route, string) {
33 if len(subdomainRoutes) == 0 {
34 return routes, ""
35 }
36@@ -185,8 +185,8 @@ func GetSshCtx(r *http.Request) (*pssh.SSHServerConnSession, error) {
37 return payload, nil
38 }
39
40-func GetCfg(r *http.Request) *ConfigSite {
41- return r.Context().Value(ctxCfg{}).(*ConfigSite)
42+func GetCfg(r *http.Request) *shared.ConfigSite {
43+ return r.Context().Value(ctxCfg{}).(*shared.ConfigSite)
44 }
45
46 func GetLogger(r *http.Request) *slog.Logger {
47@@ -213,7 +213,7 @@ func GetSubdomain(r *http.Request) string {
48 return r.Context().Value(CtxSubdomainKey{}).(string)
49 }
50
51-var txtCache = expirable.NewLRU[string, string](2048, nil, cache.CacheTimeout)
52+var txtCache = expirable.NewLRU[string, string](2048, nil, shared.CacheTimeout)
53
54 func GetCustomDomain(host string, space string) string {
55 txt := fmt.Sprintf("_%s.%s", space, host)
1@@ -7,7 +7,6 @@ import (
2 "time"
3
4 "github.com/picosh/pico/pkg/db"
5- "github.com/picosh/utils"
6 "golang.org/x/crypto/ssh"
7 )
8
9@@ -41,7 +40,7 @@ type AuthedPubkey struct {
10 }
11
12 func PubkeyCertVerify(key ssh.PublicKey, srcPrincipal string) (*AuthedPubkey, error) {
13- origPubkey := utils.KeyForKeyText(key)
14+ origPubkey := KeyForKeyText(key)
15 authed := &AuthedPubkey{
16 OrigPubkey: origPubkey,
17 Pubkey: origPubkey,
18@@ -74,7 +73,7 @@ func PubkeyCertVerify(key ssh.PublicKey, srcPrincipal string) (*AuthedPubkey, er
19 return nil, fmt.Errorf("ssh-cert has expired")
20 }
21
22- authed.Pubkey = utils.KeyForKeyText(cert.SignatureKey)
23+ authed.Pubkey = KeyForKeyText(cert.SignatureKey)
24 authed.Identity = cert.KeyId
25 return authed, nil
26 }
1@@ -4,18 +4,18 @@ import (
2 "fmt"
3 "log/slog"
4
5- "github.com/picosh/utils"
6+ "github.com/picosh/pico/pkg/shared"
7 )
8
9 func GetStorageTypeFromEnv() string {
10- return utils.GetEnv("STORAGE_ADAPTER", "fs")
11+ return shared.GetEnv("STORAGE_ADAPTER", "fs")
12 }
13
14 func NewStorage(logger *slog.Logger, adapter string) (StorageServe, error) {
15 logger.Info("storage adapter", "adapter", adapter)
16 switch adapter {
17 case "fs":
18- fsPath := utils.GetEnv("FS_STORAGE_DIR", "/tmp/pico_storage")
19+ fsPath := shared.GetEnv("FS_STORAGE_DIR", "/tmp/pico_storage")
20 logger.Info("using filesystem storage", "path", fsPath)
21 return NewStorageFS(logger, fsPath)
22 case "memory":
+4,
-4
1@@ -11,7 +11,7 @@ import (
2 "git.sr.ht/~rockorager/vaxis/vxfw/richtext"
3 "git.sr.ht/~rockorager/vaxis/vxfw/text"
4 "github.com/picosh/pico/pkg/db"
5- "github.com/picosh/utils"
6+ "github.com/picosh/pico/pkg/shared"
7 )
8
9 type SitesLoaded struct{}
10@@ -355,7 +355,7 @@ func (m *AnalyticsPage) visits(ctx vxfw.DrawContext, intervals []*db.VisitInterv
11 func (m *AnalyticsPage) fetchSites() {
12 siteList, err := m.shared.Dbpool.FindVisitSiteList(&db.SummaryOpts{
13 UserID: m.shared.User.ID,
14- Origin: utils.StartOfMonth(),
15+ Origin: shared.StartOfMonth(),
16 })
17 if err != nil {
18 m.loadingSites = false
19@@ -376,9 +376,9 @@ func (m *AnalyticsPage) fetchSiteStats(site string, interval string) {
20 }
21
22 if interval == "day" {
23- opts.Origin = utils.StartOfMonth()
24+ opts.Origin = shared.StartOfMonth()
25 } else {
26- opts.Origin = utils.StartOfYear()
27+ opts.Origin = shared.StartOfYear()
28 }
29
30 summary, err := m.shared.Dbpool.VisitSummary(opts)
+10,
-11
1@@ -14,7 +14,6 @@ import (
2 "git.sr.ht/~rockorager/vaxis/vxfw/richtext"
3 "git.sr.ht/~rockorager/vaxis/vxfw/text"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 pipeLogger "github.com/picosh/utils/pipe/log"
7 )
8
9@@ -215,10 +214,10 @@ func (m *LogsPage) connectToLogs() error {
10 continue
11 }
12
13- user := utils.AnyToStr(parsedData, "user")
14- userId := utils.AnyToStr(parsedData, "userId")
15+ user := shared.AnyToStr(parsedData, "user")
16+ userId := shared.AnyToStr(parsedData, "userId")
17
18- hidden := utils.AnyToBool(parsedData, "hidden")
19+ hidden := shared.AnyToBool(parsedData, "hidden")
20
21 if !hidden && (user == m.shared.User.Name || userId == m.shared.User.ID) {
22 m.shared.App.PostEvent(LogLineLoaded{parsedData})
23@@ -250,13 +249,13 @@ type LogLine struct {
24 }
25
26 func NewLogLine(data map[string]any) *LogLine {
27- rawtime := utils.AnyToStr(data, "time")
28- service := utils.AnyToStr(data, "service")
29- level := utils.AnyToStr(data, "level")
30- msg := utils.AnyToStr(data, "msg")
31- errMsg := utils.AnyToStr(data, "err")
32- status := utils.AnyToFloat(data, "status")
33- url := utils.AnyToStr(data, "url")
34+ rawtime := shared.AnyToStr(data, "time")
35+ service := shared.AnyToStr(data, "service")
36+ level := shared.AnyToStr(data, "level")
37+ msg := shared.AnyToStr(data, "msg")
38+ errMsg := shared.AnyToStr(data, "err")
39+ status := shared.AnyToFloat(data, "status")
40+ url := shared.AnyToStr(data, "url")
41 date, err := time.Parse(time.RFC3339Nano, rawtime)
42 dateStr := rawtime
43 if err == nil {
+2,
-2
1@@ -11,7 +11,7 @@ import (
2 "git.sr.ht/~rockorager/vaxis/vxfw/richtext"
3 "git.sr.ht/~rockorager/vaxis/vxfw/text"
4 "github.com/picosh/pico/pkg/db"
5- "github.com/picosh/utils"
6+ "github.com/picosh/pico/pkg/shared"
7 "golang.org/x/crypto/ssh"
8 )
9
10@@ -253,7 +253,7 @@ func (m *AddKeyPage) addPubkey(pubkey string) error {
11 return err
12 }
13
14- key := utils.KeyForKeyText(pk)
15+ key := shared.KeyForKeyText(pk)
16
17 return m.shared.Dbpool.InsertPublicKey(
18 m.shared.User.ID, key, comment,
+2,
-2
1@@ -10,7 +10,7 @@ import (
2 "git.sr.ht/~rockorager/vaxis/vxfw/text"
3 "github.com/picosh/pico/pkg/db"
4 "github.com/picosh/pico/pkg/pssh"
5- "github.com/picosh/utils"
6+ "github.com/picosh/pico/pkg/shared"
7 "golang.org/x/crypto/ssh"
8 )
9
10@@ -44,7 +44,7 @@ func (m *SignupPage) createAccount(name string) (*db.User, error) {
11 if name == "" {
12 return nil, fmt.Errorf("name cannot be empty")
13 }
14- key := utils.KeyForKeyText(m.shared.Session.PublicKey())
15+ key := shared.KeyForKeyText(m.shared.Session.PublicKey())
16 return m.shared.Dbpool.RegisterUser(name, key, "")
17 }
18
+1,
-2
1@@ -14,7 +14,6 @@ import (
2 "github.com/picosh/pico/pkg/db"
3 "github.com/picosh/pico/pkg/pssh"
4 "github.com/picosh/pico/pkg/shared"
5- "github.com/picosh/utils"
6 )
7
8 const (
9@@ -289,7 +288,7 @@ func FindUser(shrd *SharedModel) (*db.User, error) {
10 return nil, fmt.Errorf("unable to find public key")
11 }
12
13- key := utils.KeyForKeyText(shrd.Session.PublicKey())
14+ key := shared.KeyForKeyText(shrd.Session.PublicKey())
15
16 user, err := shrd.Dbpool.FindUserByKey(usr, key)
17 if err != nil {