Commit c6e626e
Antonio Mika
·
2026-10-10 15:51:00 -0400 EDT
parent 949a5c0
feat(features): honor standalone flags for every service
8 files changed,
+50,
-84
+1,
-1
| ... | ... | @@ -21,7 +21,7 @@ rsync, scp, or sftp. Hopefully you see the trend. | |
| 21 | 21 | - [pipe](https://pipe.pico.sh): Authenticated `*nix` pipes over `ssh` | |
| 22 | 22 | - [prose](https://pico.sh/prose): A blog platform using `ssh` for content | |
| 23 | 23 | management. | |
| 24 | - | - [rss-to-email](https://pico.sh/feeds): An RSS email notification service using `ssh`. | |
| 24 | + | - [feeds](https://pico.sh/feeds): An RSS email notification service using `ssh`. | |
| 25 | 25 | - [pastes](https://pico.sh/pastes): Upload code snippets using rsync, scp, and | |
| 26 | 26 | sftp. | |
| 27 | 27 |
+1,
-7
| ... | ... | @@ -160,13 +160,7 @@ func Middleware(dbpool db.DB, cfg *shared.ConfigSite) pssh.SSHServerMiddleware { | |
| 160 | 160 | logger.Info("run cmd", "filename", filename) | |
| 161 | 161 | fetcher := NewFetcher(dbpool, cfg) | |
| 162 | 162 | ||
| 163 | - | isPicoPlus := false | |
| 164 | - | ff, _ := dbpool.FindFeature(user.ID, "plus") | |
| 165 | - | if ff != nil { | |
| 166 | - | if ff.IsValid() { | |
| 167 | - | isPicoPlus = true | |
| 168 | - | } | |
| 169 | - | } | |
| 163 | + | isPicoPlus := db.HasPlusOrSpace(dbpool, user.ID, "feeds") | |
| 170 | 164 | ||
| 171 | 165 | err = fetcher.RunPost(logger, user, isPicoPlus, post, true, time.Now().UTC()) | |
| 172 | 166 | if err != nil { |
+2,
-9
| ... | ... | @@ -336,16 +336,9 @@ func (f *Fetcher) RunUser(user *db.User, now time.Time) error { | |
| 336 | 336 | if err != nil { | |
| 337 | 337 | return err | |
| 338 | 338 | } | |
| 339 | - | isPicoPlus := false | |
| 340 | - | ff, _ := f.db.FindFeature(user.ID, "plus") | |
| 341 | - | if ff != nil { | |
| 342 | - | if ff.IsValid() { | |
| 343 | - | isPicoPlus = true | |
| 344 | - | } | |
| 345 | - | } | |
| 346 | - | ||
| 339 | + | isPicoPlus := db.HasPlusOrSpace(f.db, user.ID, "feeds") | |
| 347 | 340 | if !isPicoPlus { | |
| 348 | - | return fmt.Errorf("must be pico+ user to receive rss digests") | |
| 341 | + | return fmt.Errorf("must have pico+ or feeds access to receive rss digests") | |
| 349 | 342 | } | |
| 350 | 343 | ||
| 351 | 344 | if len(posts.Data) > 0 { |
+4,
-0
| ... | ... | @@ -660,3 +660,7 @@ type DB interface { | |
| 660 | 660 | ||
| 661 | 661 | Close() error | |
| 662 | 662 | } | |
| 663 | + | ||
| 664 | + | func HasPlusOrSpace(dbpool DB, userID, space string) bool { | |
| 665 | + | return dbpool.HasFeatureByUser(userID, "plus") || dbpool.HasFeatureByUser(userID, space) | |
| 666 | + | } |
| ... | ... | @@ -77,7 +77,7 @@ func (hc *ApiConfig) HasPrivilegedAccess(apiToken string) bool { | |
| 77 | 77 | } | |
| 78 | 78 | ||
| 79 | 79 | func (hc *ApiConfig) HasPlusOrSpace(user *db.User, space string) bool { | |
| 80 | - | return hc.Dbpool.HasFeatureByUser(user.ID, "plus") || hc.Dbpool.HasFeatureByUser(user.ID, space) | |
| 80 | + | return db.HasPlusOrSpace(hc.Dbpool, user.ID, space) | |
| 81 | 81 | } | |
| 82 | 82 | ||
| 83 | 83 | func (hc *ApiConfig) CreateCtx(prevCtx context.Context, subdomain string) context.Context { |
+35,
-47
| ... | ... | @@ -153,14 +153,42 @@ func (m *FeaturesList) getFeaturesKv() []Kv { | |
| 153 | 153 | } | |
| 154 | 154 | ||
| 155 | 155 | type ServicesList struct { | |
| 156 | - | plusFf *db.FeatureFlag | |
| 157 | - | pgsFf *db.FeatureFlag | |
| 158 | - | proseFf *db.FeatureFlag | |
| 159 | - | pastesFf *db.FeatureFlag | |
| 156 | + | shared *SharedModel | |
| 157 | + | data [][]string | |
| 158 | + | } | |
| 159 | + | ||
| 160 | + | func NewServicesList(shrd *SharedModel) *ServicesList { | |
| 161 | + | return &ServicesList{shared: shrd} | |
| 162 | + | } | |
| 163 | + | ||
| 164 | + | func (m *ServicesList) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Command, error) { | |
| 165 | + | switch ev.(type) { | |
| 166 | + | case vxfw.Init: | |
| 167 | + | m.fetchServices() | |
| 168 | + | return vxfw.RedrawCmd{}, nil | |
| 169 | + | } | |
| 170 | + | return nil, nil | |
| 160 | 171 | } | |
| 161 | 172 | ||
| 162 | - | func NewServicesList(plusFf *db.FeatureFlag, pgsFf *db.FeatureFlag, proseFf *db.FeatureFlag, pastesFf *db.FeatureFlag) *ServicesList { | |
| 163 | - | return &ServicesList{plusFf: plusFf, pgsFf: pgsFf, proseFf: proseFf, pastesFf: pastesFf} | |
| 173 | + | func (m *ServicesList) fetchServices() { | |
| 174 | + | dbpool := m.shared.Dbpool | |
| 175 | + | userID := m.shared.User.ID | |
| 176 | + | status := func(space, missing string) string { | |
| 177 | + | if db.HasPlusOrSpace(dbpool, userID, space) { | |
| 178 | + | return "active" | |
| 179 | + | } | |
| 180 | + | return missing | |
| 181 | + | } | |
| 182 | + | m.data = [][]string{ | |
| 183 | + | {"name", "status"}, | |
| 184 | + | {"pipe", "active"}, | |
| 185 | + | {"pastes", status("pastes", "invite/pico+")}, | |
| 186 | + | {"prose", status("prose", "invite/pico+")}, | |
| 187 | + | {"pages", status("pgs", "invite/pico+")}, | |
| 188 | + | {"tuns", status("tuns", "pico+")}, | |
| 189 | + | {"irc bouncer", status("bouncer", "pico+")}, | |
| 190 | + | {"feeds", status("feeds", "pico+")}, | |
| 191 | + | } | |
| 164 | 192 | } | |
| 165 | 193 | ||
| 166 | 194 | func (m *ServicesList) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) { |
| ... | ... | @@ -179,48 +207,8 @@ func (m *ServicesList) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) { | |
| 179 | 207 | } | |
| 180 | 208 | ||
| 181 | 209 | func (m *ServicesList) getServiceKv() []Kv { | |
| 182 | - | hasPlus := m.plusFf != nil && m.plusFf.IsValid() | |
| 183 | - | hasPgs := m.pgsFf != nil && m.pgsFf.IsValid() | |
| 184 | - | hasProse := m.proseFf != nil && m.proseFf.IsValid() | |
| 185 | - | hasPastes := m.pastesFf != nil && m.pastesFf.IsValid() | |
| 186 | - | pagesStatus := "invite/pico+" | |
| 187 | - | if hasPlus || hasPgs { | |
| 188 | - | pagesStatus = "active" | |
| 189 | - | } | |
| 190 | - | proseStatus := "invite/pico+" | |
| 191 | - | if hasPlus || hasProse { | |
| 192 | - | proseStatus = "active" | |
| 193 | - | } | |
| 194 | - | pastesStatus := "invite/pico+" | |
| 195 | - | if hasPlus || hasPastes { | |
| 196 | - | pastesStatus = "active" | |
| 197 | - | } | |
| 198 | - | data := [][]string{ | |
| 199 | - | {"name", "status"}, | |
| 200 | - | {"pipe", "active"}, | |
| 201 | - | {"pastes", pastesStatus}, | |
| 202 | - | {"prose", proseStatus}, | |
| 203 | - | {"pages", pagesStatus}, | |
| 204 | - | } | |
| 205 | - | ||
| 206 | - | if hasPlus { | |
| 207 | - | data = append( | |
| 208 | - | data, | |
| 209 | - | []string{"tuns", "active"}, | |
| 210 | - | []string{"irc bouncer", "active"}, | |
| 211 | - | []string{"rss-to-email", "active"}, | |
| 212 | - | ) | |
| 213 | - | } else { | |
| 214 | - | data = append( | |
| 215 | - | data, | |
| 216 | - | []string{"tuns", "pico+"}, | |
| 217 | - | []string{"irc bouncer", "pico+"}, | |
| 218 | - | []string{"rss-to-email", "pico+"}, | |
| 219 | - | ) | |
| 220 | - | } | |
| 221 | - | ||
| 222 | 210 | kv := []Kv{} | |
| 223 | - | for idx, d := range data { | |
| 211 | + | for idx, d := range m.data { | |
| 224 | 212 | value := d[1] | |
| 225 | 213 | var style vaxis.Style | |
| 226 | 214 | if idx == 0 { |
| ... | ... | @@ -28,6 +28,7 @@ type MenuPage struct { | |
| 28 | 28 | ||
| 29 | 29 | list list.Dynamic | |
| 30 | 30 | features *FeaturesList | |
| 31 | + | services *ServicesList | |
| 31 | 32 | stats *db.UserStats | |
| 32 | 33 | } | |
| 33 | 34 |
| ... | ... | @@ -50,6 +51,7 @@ func NewMenuPage(shrd *SharedModel) *MenuPage { | |
| 50 | 51 | m := &MenuPage{shared: shrd} | |
| 51 | 52 | m.list = list.Dynamic{Builder: getMenuWidget, DrawCursor: true} | |
| 52 | 53 | m.features = NewFeaturesList(shrd) | |
| 54 | + | m.services = NewServicesList(shrd) | |
| 53 | 55 | return m | |
| 54 | 56 | } | |
| 55 | 57 |
| ... | ... | @@ -74,8 +76,10 @@ func (m *MenuPage) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Comm | |
| 74 | 76 | case PageIn: | |
| 75 | 77 | _ = m.fetchUsageStats() | |
| 76 | 78 | cmd, _ := m.features.HandleEvent(vxfw.Init{}, phase) | |
| 79 | + | servicesCmd, _ := m.services.HandleEvent(vxfw.Init{}, phase) | |
| 77 | 80 | return vxfw.BatchCmd([]vxfw.Command{ | |
| 78 | 81 | cmd, | |
| 82 | + | servicesCmd, | |
| 79 | 83 | vxfw.FocusWidgetCmd(&m.list), | |
| 80 | 84 | }), nil | |
| 81 | 85 | case vaxis.Key: |
| ... | ... | @@ -105,12 +109,7 @@ func (m *MenuPage) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) { | |
| 105 | 109 | }, | |
| 106 | 110 | }) | |
| 107 | 111 | ||
| 108 | - | services, _ := NewServicesList( | |
| 109 | - | m.shared.PlusFeatureFlag, | |
| 110 | - | m.shared.PgsFeatureFlag, | |
| 111 | - | m.shared.ProseFeatureFlag, | |
| 112 | - | m.shared.PastesFeatureFlag, | |
| 113 | - | ).Draw(ctx) | |
| 112 | + | services, _ := m.services.Draw(ctx) | |
| 114 | 113 | features, _ := m.features.Draw(ctx) | |
| 115 | 114 | ||
| 116 | 115 | leftPane := NewGroupStack([]vxfw.Surface{ |
| ... | ... | @@ -129,7 +128,7 @@ func (m *MenuPage) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) { | |
| 129 | 128 | pages, _ := NewUsageInfo("pages", &m.stats.Pages).Draw(ctx) | |
| 130 | 129 | prose, _ := NewUsageInfo("prose", &m.stats.Prose).Draw(ctx) | |
| 131 | 130 | pastes, _ := NewUsageInfo("pastes", &m.stats.Pastes).Draw(ctx) | |
| 132 | - | feeds, _ := NewUsageInfo("rss-to-email", &m.stats.Feeds).Draw(ctx) | |
| 131 | + | feeds, _ := NewUsageInfo("feeds", &m.stats.Feeds).Draw(ctx) | |
| 133 | 132 | right = append(right, | |
| 134 | 133 | pages, | |
| 135 | 134 | prose, |
+0,
-12
| ... | ... | @@ -31,9 +31,6 @@ type SharedModel struct { | |
| 31 | 31 | InvitedBy string | |
| 32 | 32 | PlusFeatureFlag *db.FeatureFlag | |
| 33 | 33 | BouncerFeatureFlag *db.FeatureFlag | |
| 34 | - | PgsFeatureFlag *db.FeatureFlag | |
| 35 | - | ProseFeatureFlag *db.FeatureFlag | |
| 36 | - | PastesFeatureFlag *db.FeatureFlag | |
| 37 | 34 | Impersonator string | |
| 38 | 35 | App *vxfw.App | |
| 39 | 36 | } |
| ... | ... | @@ -277,15 +274,6 @@ func initData(shrd *SharedModel) error { | |
| 277 | 274 | bff, _ := FindFeatureFlag(shrd, "bouncer") | |
| 278 | 275 | shrd.BouncerFeatureFlag = bff | |
| 279 | 276 | ||
| 280 | - | pff, _ := FindFeatureFlag(shrd, "pgs") | |
| 281 | - | shrd.PgsFeatureFlag = pff | |
| 282 | - | ||
| 283 | - | prosef, _ := FindFeatureFlag(shrd, "prose") | |
| 284 | - | shrd.ProseFeatureFlag = prosef | |
| 285 | - | ||
| 286 | - | pastesf, _ := FindFeatureFlag(shrd, "pastes") | |
| 287 | - | shrd.PastesFeatureFlag = pastesf | |
| 288 | - | ||
| 289 | 277 | if shrd.User != nil { | |
| 290 | 278 | invitedBy, _ := shrd.Dbpool.FindWhoInvitedUser(user.ID) | |
| 291 | 279 | shrd.InvitedBy = invitedBy |