repos / pico

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

pico / pkg / shared
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}