Eric Bower
·
2025-07-04
config.go
1package shared
2
3import (
4 "context"
5 "fmt"
6 "html/template"
7 "log/slog"
8 "net/http"
9 "net/url"
10 "os"
11 "path"
12 "strings"
13 "time"
14
15 "github.com/picosh/pico/pkg/db"
16
17 pipeLogger "github.com/picosh/utils/pipe/log"
18)
19
20var DefaultEmail = "hello@pico.sh"
21
22type SitePageData struct {
23 Domain template.URL
24 HomeURL template.URL
25}
26
27type PageData struct {
28 Site SitePageData
29}
30
31type ConfigSite struct {
32 Debug bool
33 SendgridKey string
34 Domain string
35 Port string
36 PortOverride string
37 Protocol string
38 DbURL string
39 Space string
40 Issuer string
41 Secret string
42 SecretWebhook string
43 AllowedExt []string
44 HiddenPosts []string
45 MaxSize uint64
46 MaxAssetSize int64
47 MaxSpecialFileSize int64
48 Logger *slog.Logger
49 TunsSecret string
50}
51
52func NewConfigSite() *ConfigSite {
53 return &ConfigSite{}
54}
55
56type CreateURL struct {
57 Subdomain bool
58 UsernameInRoute bool
59 HostDomain string
60 AppDomain string
61 Username string
62 Cfg *ConfigSite
63}
64
65func NewCreateURL(cfg *ConfigSite) *CreateURL {
66 return &CreateURL{
67 Cfg: cfg,
68 Subdomain: cfg.IsSubdomains(),
69 }
70}
71
72func CreateURLFromRequest(cfg *ConfigSite, r *http.Request) *CreateURL {
73 hostDomain := strings.Split(r.Host, ":")[0]
74 appDomain := strings.Split(cfg.Domain, ":")[0]
75
76 onSubdomain := cfg.IsSubdomains() && strings.Contains(hostDomain, appDomain)
77 withUserName := !cfg.IsCustomdomains() || (!onSubdomain && hostDomain == appDomain)
78
79 return &CreateURL{
80 Cfg: cfg,
81 AppDomain: appDomain,
82 HostDomain: hostDomain,
83 Subdomain: onSubdomain,
84 UsernameInRoute: withUserName,
85 }
86}
87
88func (c *ConfigSite) GetSiteData() *SitePageData {
89 return &SitePageData{
90 Domain: template.URL(c.Domain),
91 HomeURL: template.URL(c.HomeURL()),
92 }
93}
94
95func (c *ConfigSite) IsSubdomains() bool {
96 return true
97}
98
99func (c *ConfigSite) IsCustomdomains() bool {
100 return true
101}
102
103func (c *ConfigSite) HomeURL() string {
104 if c.IsSubdomains() || c.IsCustomdomains() {
105 return fmt.Sprintf("//%s", c.Domain)
106 }
107
108 return "/"
109}
110
111func (c *ConfigSite) ReadURL() string {
112 if c.IsSubdomains() || c.IsCustomdomains() {
113 return fmt.Sprintf("%s://%s", c.Protocol, c.Domain)
114 }
115
116 return "/"
117}
118
119func (c *ConfigSite) StaticPath(fname string) string {
120 return path.Join("pkg", "apps", c.Space, fname)
121}
122
123func (c *ConfigSite) BlogURL(username string) string {
124 if c.IsSubdomains() {
125 return fmt.Sprintf("%s://%s.%s", c.Protocol, username, c.Domain)
126 }
127
128 return fmt.Sprintf("/%s", username)
129}
130
131func (c *ConfigSite) CssURL(username string) string {
132 if c.IsSubdomains() || c.IsCustomdomains() {
133 return "/_styles.css"
134 }
135
136 return fmt.Sprintf("/%s/styles.css", username)
137}
138
139func (c *ConfigSite) PostURL(username, slug string) string {
140 fname := url.PathEscape(slug)
141 if c.IsSubdomains() {
142 return fmt.Sprintf("%s://%s.%s/%s", c.Protocol, username, c.Domain, fname)
143 }
144
145 return fmt.Sprintf("/%s/%s", username, fname)
146
147}
148
149func (c *ConfigSite) RawPostURL(username, slug string) string {
150 fname := url.PathEscape(slug)
151 if c.IsSubdomains() {
152 return fmt.Sprintf("%s://%s.%s/raw/%s", c.Protocol, username, c.Domain, fname)
153 }
154
155 return fmt.Sprintf("/raw/%s/%s", username, fname)
156}
157
158func (c *ConfigSite) ImgFullURL(username, slug string) string {
159 fname := url.PathEscape(strings.TrimLeft(slug, "/"))
160 return fmt.Sprintf("%s://%s.%s/%s", c.Protocol, username, c.Domain, fname)
161}
162
163func (c *ConfigSite) FullBlogURL(curl *CreateURL, username string) string {
164 if c.IsSubdomains() && curl.Subdomain {
165 return fmt.Sprintf("%s://%s.%s", c.Protocol, username, c.Domain)
166 }
167
168 if curl.UsernameInRoute {
169 return fmt.Sprintf("/%s", username)
170 }
171
172 return fmt.Sprintf("%s://%s", c.Protocol, curl.HostDomain)
173}
174
175func (c *ConfigSite) FullPostURL(curl *CreateURL, username, slug string) string {
176 fname := url.PathEscape(strings.TrimLeft(slug, "/"))
177
178 if curl.Subdomain && c.IsSubdomains() {
179 return fmt.Sprintf("%s://%s.%s/%s", c.Protocol, username, c.Domain, fname)
180 }
181
182 if curl.UsernameInRoute {
183 return fmt.Sprintf("%s://%s/%s/%s", c.Protocol, c.Domain, username, fname)
184 }
185
186 return fmt.Sprintf("%s://%s/%s", c.Protocol, curl.HostDomain, fname)
187}
188
189func (c *ConfigSite) RssBlogURL(curl *CreateURL, username, tag string) string {
190 url := ""
191 if c.IsSubdomains() && curl.Subdomain {
192 url = fmt.Sprintf("%s://%s.%s/rss", c.Protocol, username, c.Domain)
193 } else if curl.UsernameInRoute {
194 url = fmt.Sprintf("/%s/rss", username)
195 } else {
196 url = "/rss"
197 }
198
199 if tag != "" {
200 return fmt.Sprintf("%s?tag=%s", url, tag)
201 }
202
203 return url
204}
205
206func (c *ConfigSite) ImgURL(curl *CreateURL, username string, slug string) string {
207 fname := url.PathEscape(strings.TrimLeft(slug, "/"))
208 if c.IsSubdomains() && curl.Subdomain {
209 return fmt.Sprintf("%s://%s.%s/%s", c.Protocol, username, c.Domain, fname)
210 }
211
212 if curl.UsernameInRoute {
213 return fmt.Sprintf("/%s/%s", username, fname)
214 }
215
216 return fmt.Sprintf("/%s", fname)
217}
218
219func (c *ConfigSite) ImgPostURL(curl *CreateURL, username string, slug string) string {
220 fname := url.PathEscape(strings.TrimLeft(slug, "/"))
221 if c.IsSubdomains() && curl.Subdomain {
222 return fmt.Sprintf("%s://%s.%s/p/%s", c.Protocol, username, c.Domain, fname)
223 }
224
225 if curl.UsernameInRoute {
226 return fmt.Sprintf("/%s/p/%s", username, fname)
227 }
228
229 return fmt.Sprintf("/p/%s", fname)
230}
231
232func (c *ConfigSite) ImgOrigURL(curl *CreateURL, username string, slug string) string {
233 fname := url.PathEscape(strings.TrimLeft(slug, "/"))
234 if c.IsSubdomains() && curl.Subdomain {
235 return fmt.Sprintf("%s://%s.%s/o/%s", c.Protocol, username, c.Domain, fname)
236 }
237
238 if curl.UsernameInRoute {
239 return fmt.Sprintf("/%s/o/%s", username, fname)
240 }
241
242 return fmt.Sprintf("/o/%s", fname)
243}
244
245func (c *ConfigSite) TagURL(curl *CreateURL, username, tag string) string {
246 tg := url.PathEscape(tag)
247 return fmt.Sprintf("%s?tag=%s", c.FullBlogURL(curl, username), tg)
248}
249
250func (c *ConfigSite) AssetURL(username, projectName, fpath string) string {
251 if username == projectName {
252 return fmt.Sprintf(
253 "%s://%s.%s/%s",
254 c.Protocol,
255 username,
256 c.Domain,
257 fpath,
258 )
259 }
260
261 return fmt.Sprintf(
262 "%s://%s-%s.%s/%s",
263 c.Protocol,
264 username,
265 projectName,
266 c.Domain,
267 fpath,
268 )
269}
270
271func CreateLogger(space string, withPipe bool) *slog.Logger {
272 logger := slog.New(
273 slog.NewTextHandler(
274 os.Stdout,
275 &slog.HandlerOptions{
276 AddSource: true,
277 Level: slog.LevelInfo,
278 },
279 ),
280 )
281
282 if withPipe {
283 conn := NewPicoPipeClient()
284 logger = pipeLogger.RegisterReconnectLogger(context.Background(), logger, conn, 100, 10*time.Millisecond)
285 }
286
287 logger = logger.With("service", space)
288
289 hostname, err := os.Hostname()
290 if err == nil && hostname != "" {
291 logger = logger.With("hostname", hostname)
292 }
293
294 nodename := os.Getenv("NODENAME")
295 if nodename != "" {
296 logger = logger.With("nodename", nodename)
297 }
298
299 slog.SetDefault(logger)
300
301 return logger
302}
303
304func LoggerWithUser(logger *slog.Logger, user *db.User) *slog.Logger {
305 return logger.With("user", user.Name, "userId", user.ID)
306}