Antonio Mika
·
2026-10-10
1package shared
2
3import (
4 "context"
5 "fmt"
6 "html/template"
7 "log/slog"
8 "net/http"
9 "net/url"
10 "os"
11 "strings"
12 "time"
13
14 "github.com/picosh/pico/pkg/db"
15
16 pipeLogger "github.com/picosh/utils/pipe/log"
17)
18
19var DefaultEmail = "hello@pico.sh"
20
21type SitePageData struct {
22 Domain template.URL
23 HomeURL template.URL
24}
25
26type PageData struct {
27 Site SitePageData
28}
29
30// CreateURL builds links to users' sites on Domain, as seen from the host a
31// request came in on.
32type CreateURL struct {
33 Protocol string
34 Domain string
35 Subdomain bool
36 UsernameInRoute bool
37 HostDomain string
38}
39
40// NewCreateURL builds links that point at users' subdomains.
41func NewCreateURL(protocol, domain string) *CreateURL {
42 return &CreateURL{
43 Protocol: protocol,
44 Domain: domain,
45 Subdomain: true,
46 }
47}
48
49func CreateURLFromRequest(protocol, domain string, r *http.Request) *CreateURL {
50 hostDomain := strings.Split(r.Host, ":")[0]
51 appDomain := strings.Split(domain, ":")[0]
52
53 onSubdomain := strings.Contains(hostDomain, appDomain)
54 withUserName := !onSubdomain && hostDomain == appDomain
55
56 return &CreateURL{
57 Protocol: protocol,
58 Domain: domain,
59 HostDomain: hostDomain,
60 Subdomain: onSubdomain,
61 UsernameInRoute: withUserName,
62 }
63}
64
65func (c *CreateURL) FullBlogURL(username string) string {
66 if c.Subdomain {
67 return fmt.Sprintf("%s://%s.%s", c.Protocol, username, c.Domain)
68 }
69
70 if c.UsernameInRoute {
71 return fmt.Sprintf("/%s", username)
72 }
73
74 return fmt.Sprintf("%s://%s", c.Protocol, c.HostDomain)
75}
76
77func (c *CreateURL) FullPostURL(username, slug string) string {
78 fname := url.PathEscape(strings.TrimLeft(slug, "/"))
79
80 if c.Subdomain {
81 return fmt.Sprintf("%s://%s.%s/%s", c.Protocol, username, c.Domain, fname)
82 }
83
84 if c.UsernameInRoute {
85 return fmt.Sprintf("%s://%s/%s/%s", c.Protocol, c.Domain, username, fname)
86 }
87
88 return fmt.Sprintf("%s://%s/%s", c.Protocol, c.HostDomain, fname)
89}
90
91func (c *CreateURL) RssBlogURL(username, tag string) string {
92 url := ""
93 if c.Subdomain {
94 url = fmt.Sprintf("%s://%s.%s/rss", c.Protocol, username, c.Domain)
95 } else if c.UsernameInRoute {
96 url = fmt.Sprintf("/%s/rss", username)
97 } else {
98 url = "/rss"
99 }
100
101 if tag != "" {
102 return fmt.Sprintf("%s?tag=%s", url, tag)
103 }
104
105 return url
106}
107
108func CreateLogger(space string, withPipe bool) *slog.Logger {
109 logger := slog.New(
110 slog.NewTextHandler(
111 os.Stdout,
112 &slog.HandlerOptions{
113 AddSource: true,
114 Level: slog.LevelInfo,
115 },
116 ),
117 )
118
119 if withPipe {
120 conn := NewPicoPipeClient()
121 logger = pipeLogger.RegisterReconnectLogger(context.Background(), logger, conn, 100, 10*time.Millisecond)
122 }
123
124 logger = logger.With("service", space)
125
126 hostname, err := os.Hostname()
127 if err == nil && hostname != "" {
128 logger = logger.With("hostname", hostname)
129 }
130
131 nodename := os.Getenv("NODENAME")
132 if nodename != "" {
133 logger = logger.With("nodename", nodename)
134 }
135
136 slog.SetDefault(logger)
137
138 return logger
139}
140
141func LoggerWithUser(logger *slog.Logger, user *db.User) *slog.Logger {
142 return logger.With("user", user.Name, "userId", user.ID)
143}