repos / pico

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

pico / pkg / shared
Eric Bower  ·  2025-03-17

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