- commit
- f08fc05
- parent
- 7deb53b
- author
- Eric Bower
- date
- 2023-08-17 16:43:27 +0000 UTC
refactor: move Cmd to own file
3 files changed,
+319,
-315
M
Makefile
+1,
-1
1@@ -74,7 +74,7 @@ pgs-deploy: pgs-static pgs-site
2 scp -R ./public/* hey@pgs.sh:/pgs-prod
3 .PHONY: pgs-site-deploy
4
5-format:
6+fmt:
7 go fmt ./...
8 .PHONY: format
9
+318,
-0
1@@ -0,0 +1,318 @@
2+package pgs
3+
4+import (
5+ "errors"
6+ "fmt"
7+
8+ "github.com/gliderlabs/ssh"
9+ "github.com/picosh/pico/db"
10+ "github.com/picosh/pico/shared"
11+ "github.com/picosh/pico/shared/storage"
12+ "github.com/picosh/pico/wish/send/utils"
13+ "go.uber.org/zap"
14+)
15+
16+func getHelpText(userName, projectName string) string {
17+ helpStr := "commands: [help, stats, ls, rm, link, unlink, prune, retain, depends]\n\n"
18+ sshCmdStr := fmt.Sprintf("ssh %s@pgs.sh", userName)
19+ helpStr += fmt.Sprintf("`%s help`: prints this screen\n", sshCmdStr)
20+ helpStr += fmt.Sprintf("`%s stats`: prints stats for user\n", sshCmdStr)
21+ helpStr += fmt.Sprintf("`%s ls`: lists projects\n", sshCmdStr)
22+ helpStr += fmt.Sprintf("`%s rm %s`: deletes `%s`\n", sshCmdStr, projectName, projectName)
23+ helpStr += fmt.Sprintf("`%s link %s project-b`: symbolic link from `%s` to `project-b`\n", sshCmdStr, projectName, projectName)
24+ helpStr += fmt.Sprintf(
25+ "`%s unlink %s`: alias for `link %s %s`, which removes symbolic link for `%s`\n",
26+ sshCmdStr, projectName, projectName, projectName, projectName,
27+ )
28+ helpStr += fmt.Sprintf("`%s prune %s`: removes all projects that match prefix `%s` and is not linked to another project\n", sshCmdStr, projectName, projectName)
29+ helpStr += fmt.Sprintf("`%s retain %s`: alias for `prune` but retains the (3) most recently updated projects\n", sshCmdStr, projectName)
30+ helpStr += fmt.Sprintf("`%s depends %s`: lists all projects linked to `%s`\n", sshCmdStr, projectName, projectName)
31+ return helpStr
32+}
33+
34+type Cmd struct {
35+ user *db.User
36+ session ssh.Session
37+ log *zap.SugaredLogger
38+ store storage.ObjectStorage
39+ dbpool db.DB
40+ write bool
41+}
42+
43+func (c *Cmd) rmProjectAssets(projectName string) error {
44+ bucketName := shared.GetAssetBucketName(c.user.ID)
45+ bucket, err := c.store.GetBucket(bucketName)
46+ if err != nil {
47+ return err
48+ }
49+
50+ fileList, err := c.store.ListFiles(bucket, projectName+"/", true)
51+ if err != nil {
52+ return err
53+ }
54+
55+ for _, file := range fileList {
56+ intent := []byte(fmt.Sprintf("deleted (%s)\n", file.Name()))
57+ if c.write {
58+ err = c.store.DeleteFile(bucket, file.Name())
59+ if err == nil {
60+ _, _ = c.session.Write(intent)
61+ } else {
62+ return err
63+ }
64+ } else {
65+ _, _ = c.session.Write(intent)
66+ }
67+ }
68+ return nil
69+}
70+
71+func (c *Cmd) help() {
72+ _, _ = c.session.Write([]byte(getHelpText(c.user.Name, "project-a")))
73+}
74+
75+func (c *Cmd) stats(maxSize int) error {
76+ bucketName := shared.GetAssetBucketName(c.user.ID)
77+ bucket, err := c.store.UpsertBucket(bucketName)
78+ if err != nil {
79+ return err
80+ }
81+
82+ totalFileSize, err := c.store.GetBucketQuota(bucket)
83+ if err != nil {
84+ return err
85+ }
86+
87+ projects, err := c.dbpool.FindProjectsByUser(c.user.ID)
88+ if err != nil {
89+ return err
90+ }
91+
92+ str := "stats\n"
93+ str += "=====\n"
94+ str += fmt.Sprintf(
95+ "space:\t\t%.4f/%.4fGB, %.4f%%\n",
96+ shared.BytesToGB(int(totalFileSize)),
97+ shared.BytesToGB(maxSize),
98+ (float32(totalFileSize)/float32(maxSize))*100,
99+ )
100+ str += fmt.Sprintf("projects:\t%d\n", len(projects))
101+ _, _ = c.session.Write([]byte(str))
102+
103+ return nil
104+}
105+
106+func (c *Cmd) ls() error {
107+ projects, err := c.dbpool.FindProjectsByUser(c.user.ID)
108+ if err != nil {
109+ return err
110+ }
111+
112+ if len(projects) == 0 {
113+ out := "no linked projects found\n"
114+ _, _ = c.session.Write([]byte(out))
115+ }
116+
117+ for _, project := range projects {
118+ out := fmt.Sprintf("%s (links to: %s)\n", project.Name, project.ProjectDir)
119+ if project.Name == project.ProjectDir {
120+ out = fmt.Sprintf("%s\n", project.Name)
121+ }
122+ _, _ = c.session.Write([]byte(out))
123+ }
124+
125+ return nil
126+}
127+
128+func (c *Cmd) unlink(projectName string) error {
129+ c.log.Infof("user (%s) running `unlink` command with (%s)", c.user.Name, projectName)
130+ project, err := c.dbpool.FindProjectByName(c.user.ID, projectName)
131+ if err != nil {
132+ return errors.Join(err, fmt.Errorf("project (%s) does not exit", projectName))
133+ }
134+
135+ err = c.dbpool.LinkToProject(c.user.ID, project.ID, project.Name, c.write)
136+ if err != nil {
137+ return err
138+ }
139+
140+ return nil
141+}
142+
143+func (c *Cmd) link(projectName, linkTo string) error {
144+ c.log.Infof("user (%s) running `link` command with (%s) (%s)", c.user.Name, projectName, linkTo)
145+
146+ projectDir := linkTo
147+ _, err := c.dbpool.FindProjectByName(c.user.ID, linkTo)
148+ if err != nil {
149+ e := fmt.Errorf("(%s) project doesn't exist", linkTo)
150+ return e
151+ }
152+
153+ project, err := c.dbpool.FindProjectByName(c.user.ID, projectName)
154+ projectID := ""
155+ if err == nil {
156+ projectID = project.ID
157+ c.log.Infof("user (%s) already has project (%s), updating ...", c.user.Name, projectName)
158+ err = c.dbpool.LinkToProject(c.user.ID, project.ID, projectDir, c.write)
159+ if err != nil {
160+ return err
161+ }
162+ } else {
163+ c.log.Infof("user (%s) has no project record (%s), creating ...", c.user.Name, projectName)
164+ if !c.write {
165+ out := fmt.Sprintf("(%s) cannot create a new project without `--write` permission, aborting ...\n", projectName)
166+ _, _ = c.session.Write([]byte(out))
167+ return nil
168+ }
169+ id, err := c.dbpool.InsertProject(c.user.ID, projectName, projectName)
170+ if err != nil {
171+ return err
172+ }
173+ projectID = id
174+ }
175+
176+ c.log.Infof("user (%s) linking (%s) to (%s) ...", c.user.Name, projectName, projectDir)
177+ err = c.dbpool.LinkToProject(c.user.ID, projectID, projectDir, c.write)
178+ if err != nil {
179+ return err
180+ }
181+
182+ out := fmt.Sprintf("(%s) might have orphaned assets, removing ...\n", projectName)
183+ _, _ = c.session.Write([]byte(out))
184+
185+ err = c.rmProjectAssets(projectName)
186+ if err != nil {
187+ return err
188+ }
189+
190+ out = fmt.Sprintf("(%s) now points to (%s)\n", projectName, linkTo)
191+ _, _ = c.session.Write([]byte(out))
192+ return nil
193+}
194+
195+func (c *Cmd) depends(projectName string) error {
196+ projects, err := c.dbpool.FindProjectLinks(c.user.ID, projectName)
197+ if err != nil {
198+ return err
199+ }
200+
201+ if len(projects) == 0 {
202+ out := fmt.Sprintf("no projects linked to this project (%s) found\n", projectName)
203+ _, _ = c.session.Write([]byte(out))
204+ return nil
205+ }
206+
207+ for _, project := range projects {
208+ out := fmt.Sprintf("%s (links to: %s)\n", project.Name, project.ProjectDir)
209+ if project.Name == project.ProjectDir {
210+ out = fmt.Sprintf("%s\n", project.Name)
211+ }
212+ _, _ = c.session.Write([]byte(out))
213+ }
214+
215+ return nil
216+}
217+
218+// delete all the projects and associated assets matching prefix
219+// but keep the latest N records
220+func (c *Cmd) prune(prefix string, keepNumLatest int) error {
221+ c.log.Infof("user (%s) running `clean` command for (%s)", c.user.Name, prefix)
222+ if prefix == "" || prefix == "*" {
223+ e := fmt.Errorf("must provide valid prefix")
224+ return e
225+ }
226+
227+ projects, err := c.dbpool.FindProjectsByPrefix(c.user.ID, prefix)
228+ if err != nil {
229+ return err
230+ }
231+
232+ rmProjects := []*db.Project{}
233+ for _, project := range projects {
234+ links, err := c.dbpool.FindProjectLinks(c.user.ID, project.Name)
235+ if err != nil {
236+ return err
237+ }
238+
239+ if len(links) == 0 {
240+ out := fmt.Sprintf("project (%s) is available to delete\n", project.Name)
241+ _, _ = c.session.Write([]byte(out))
242+ rmProjects = append(rmProjects, project)
243+ }
244+ }
245+
246+ goodbye := rmProjects
247+ if keepNumLatest > 0 {
248+ goodbye = rmProjects[:len(rmProjects)-keepNumLatest]
249+ }
250+
251+ for _, project := range goodbye {
252+ err = c.rmProjectAssets(project.Name)
253+ if err != nil {
254+ return err
255+ }
256+
257+ out := fmt.Sprintf("(%s) removing ...\n", project.Name)
258+ _, _ = c.session.Write([]byte(out))
259+
260+ if c.write {
261+ c.log.Infof("(%s) removing ...", project.Name)
262+ err = c.dbpool.RemoveProject(project.ID)
263+ if err != nil {
264+ return err
265+ }
266+ }
267+ }
268+
269+ return nil
270+}
271+
272+func (c *Cmd) rm(projectName string) error {
273+ c.log.Infof("user (%s) running `rm` command for (%s)", c.user.Name, projectName)
274+ project, err := c.dbpool.FindProjectByName(c.user.ID, projectName)
275+ if err == nil {
276+ c.log.Infof("found project (%s) (%s), checking dependencies ...", projectName, project.ID)
277+
278+ links, err := c.dbpool.FindProjectLinks(c.user.ID, projectName)
279+ if err != nil {
280+ return err
281+ }
282+
283+ if len(links) > 0 {
284+ e := fmt.Errorf("project (%s) has (%d) other projects linking to it, cannot delete project until they have been unlinked or removed, aborting ...", projectName, len(links))
285+ return e
286+ }
287+
288+ out := fmt.Sprintf("(%s) removing ...\n", project.Name)
289+ _, _ = c.session.Write([]byte(out))
290+ if c.write {
291+ c.log.Infof("(%s) removing ...", project.Name)
292+ err = c.dbpool.RemoveProject(project.ID)
293+ if err != nil {
294+ return err
295+ }
296+ }
297+ } else {
298+ e := fmt.Errorf("(%s) project not found for user (%s)", projectName, c.user.Name)
299+ return e
300+ }
301+
302+ err = c.rmProjectAssets(project.Name)
303+ return err
304+}
305+
306+func (c *Cmd) bail(err error) {
307+ if err == nil {
308+ return
309+ }
310+ c.log.Error(err)
311+ utils.ErrorHandler(c.session, err)
312+}
313+
314+func (c *Cmd) notice() {
315+ if !c.write {
316+ out := fmt.Sprintf("\nNOTICE: changes not commited, use `--write` to save operation\n")
317+ _, _ = c.session.Write([]byte(out))
318+ }
319+}
+0,
-314
1@@ -1,7 +1,6 @@
2 package pgs
3
4 import (
5- "errors"
6 "fmt"
7 "strings"
8
9@@ -9,11 +8,8 @@ import (
10 "github.com/gliderlabs/ssh"
11 "github.com/picosh/pico/db"
12 uploadassets "github.com/picosh/pico/filehandlers/assets"
13- "github.com/picosh/pico/shared"
14- "github.com/picosh/pico/shared/storage"
15 "github.com/picosh/pico/wish/cms/util"
16 "github.com/picosh/pico/wish/send/utils"
17- "go.uber.org/zap"
18 )
19
20 func getUser(s ssh.Session, dbpool db.DB) (*db.User, error) {
21@@ -35,316 +31,6 @@ func getUser(s ssh.Session, dbpool db.DB) (*db.User, error) {
22 return user, nil
23 }
24
25-type ProjectDetails struct {
26- session ssh.Session
27- store storage.ObjectStorage
28-}
29-
30-func getHelpText(userName, projectName string) string {
31- helpStr := "commands: [help, stats, ls, rm, link, unlink, prune, retain, depends]\n\n"
32- sshCmdStr := fmt.Sprintf("ssh %s@pgs.sh", userName)
33- helpStr += fmt.Sprintf("`%s help`: prints this screen\n", sshCmdStr)
34- helpStr += fmt.Sprintf("`%s stats`: prints stats for user\n", sshCmdStr)
35- helpStr += fmt.Sprintf("`%s ls`: lists projects\n", sshCmdStr)
36- helpStr += fmt.Sprintf("`%s rm %s`: deletes `%s`\n", sshCmdStr, projectName, projectName)
37- helpStr += fmt.Sprintf("`%s link %s project-b`: symbolic link from `%s` to `project-b`\n", sshCmdStr, projectName, projectName)
38- helpStr += fmt.Sprintf(
39- "`%s unlink %s`: alias for `link %s %s`, which removes symbolic link for `%s`\n",
40- sshCmdStr, projectName, projectName, projectName, projectName,
41- )
42- helpStr += fmt.Sprintf("`%s prune %s`: removes all projects that match prefix `%s` and is not linked to another project\n", sshCmdStr, projectName, projectName)
43- helpStr += fmt.Sprintf("`%s retain %s`: alias for `prune` but retains the (3) most recently updated projects\n", sshCmdStr, projectName)
44- helpStr += fmt.Sprintf("`%s depends %s`: lists all projects linked to `%s`\n", sshCmdStr, projectName, projectName)
45- return helpStr
46-}
47-
48-type Cmd struct {
49- user *db.User
50- session ssh.Session
51- log *zap.SugaredLogger
52- store storage.ObjectStorage
53- dbpool db.DB
54- write bool
55-}
56-
57-func (c *Cmd) rmProjectAssets(projectName string) error {
58- bucketName := shared.GetAssetBucketName(c.user.ID)
59- bucket, err := c.store.GetBucket(bucketName)
60- if err != nil {
61- return err
62- }
63-
64- fileList, err := c.store.ListFiles(bucket, projectName+"/", true)
65- if err != nil {
66- return err
67- }
68-
69- for _, file := range fileList {
70- intent := []byte(fmt.Sprintf("deleted (%s)\n", file.Name()))
71- if c.write {
72- err = c.store.DeleteFile(bucket, file.Name())
73- if err == nil {
74- _, _ = c.session.Write(intent)
75- } else {
76- return err
77- }
78- } else {
79- _, _ = c.session.Write(intent)
80- }
81- }
82- return nil
83-}
84-
85-func (c *Cmd) help() {
86- _, _ = c.session.Write([]byte(getHelpText(c.user.Name, "project-a")))
87-}
88-
89-func (c *Cmd) stats(maxSize int) error {
90- bucketName := shared.GetAssetBucketName(c.user.ID)
91- bucket, err := c.store.UpsertBucket(bucketName)
92- if err != nil {
93- return err
94- }
95-
96- totalFileSize, err := c.store.GetBucketQuota(bucket)
97- if err != nil {
98- return err
99- }
100-
101- projects, err := c.dbpool.FindProjectsByUser(c.user.ID)
102- if err != nil {
103- return err
104- }
105-
106- str := "stats\n"
107- str += "=====\n"
108- str += fmt.Sprintf(
109- "space:\t\t%.4f/%.4fGB, %.4f%%\n",
110- shared.BytesToGB(int(totalFileSize)),
111- shared.BytesToGB(maxSize),
112- (float32(totalFileSize)/float32(maxSize))*100,
113- )
114- str += fmt.Sprintf("projects:\t%d\n", len(projects))
115- _, _ = c.session.Write([]byte(str))
116-
117- return nil
118-}
119-
120-func (c *Cmd) ls() error {
121- projects, err := c.dbpool.FindProjectsByUser(c.user.ID)
122- if err != nil {
123- return err
124- }
125-
126- if len(projects) == 0 {
127- out := "no linked projects found\n"
128- _, _ = c.session.Write([]byte(out))
129- }
130-
131- for _, project := range projects {
132- out := fmt.Sprintf("%s (links to: %s)\n", project.Name, project.ProjectDir)
133- if project.Name == project.ProjectDir {
134- out = fmt.Sprintf("%s\n", project.Name)
135- }
136- _, _ = c.session.Write([]byte(out))
137- }
138-
139- return nil
140-}
141-
142-func (c *Cmd) unlink(projectName string) error {
143- c.log.Infof("user (%s) running `unlink` command with (%s)", c.user.Name, projectName)
144- project, err := c.dbpool.FindProjectByName(c.user.ID, projectName)
145- if err != nil {
146- return errors.Join(err, fmt.Errorf("project (%s) does not exit", projectName))
147- }
148-
149- err = c.dbpool.LinkToProject(c.user.ID, project.ID, project.Name, c.write)
150- if err != nil {
151- return err
152- }
153-
154- return nil
155-}
156-
157-func (c *Cmd) link(projectName, linkTo string) error {
158- c.log.Infof("user (%s) running `link` command with (%s) (%s)", c.user.Name, projectName, linkTo)
159-
160- projectDir := linkTo
161- _, err := c.dbpool.FindProjectByName(c.user.ID, linkTo)
162- if err != nil {
163- e := fmt.Errorf("(%s) project doesn't exist", linkTo)
164- return e
165- }
166-
167- project, err := c.dbpool.FindProjectByName(c.user.ID, projectName)
168- projectID := ""
169- if err == nil {
170- projectID = project.ID
171- c.log.Infof("user (%s) already has project (%s), updating ...", c.user.Name, projectName)
172- err = c.dbpool.LinkToProject(c.user.ID, project.ID, projectDir, c.write)
173- if err != nil {
174- return err
175- }
176- } else {
177- c.log.Infof("user (%s) has no project record (%s), creating ...", c.user.Name, projectName)
178- if !c.write {
179- out := fmt.Sprintf("(%s) cannot create a new project without `--write` permission, aborting ...\n", projectName)
180- _, _ = c.session.Write([]byte(out))
181- return nil
182- }
183- id, err := c.dbpool.InsertProject(c.user.ID, projectName, projectName)
184- if err != nil {
185- return err
186- }
187- projectID = id
188- }
189-
190- c.log.Infof("user (%s) linking (%s) to (%s) ...", c.user.Name, projectName, projectDir)
191- err = c.dbpool.LinkToProject(c.user.ID, projectID, projectDir, c.write)
192- if err != nil {
193- return err
194- }
195-
196- out := fmt.Sprintf("(%s) might have orphaned assets, removing ...\n", projectName)
197- _, _ = c.session.Write([]byte(out))
198-
199- err = c.rmProjectAssets(projectName)
200- if err != nil {
201- return err
202- }
203-
204- out = fmt.Sprintf("(%s) now points to (%s)\n", projectName, linkTo)
205- _, _ = c.session.Write([]byte(out))
206- return nil
207-}
208-
209-func (c *Cmd) depends(projectName string) error {
210- projects, err := c.dbpool.FindProjectLinks(c.user.ID, projectName)
211- if err != nil {
212- return err
213- }
214-
215- if len(projects) == 0 {
216- out := fmt.Sprintf("no projects linked to this project (%s) found\n", projectName)
217- _, _ = c.session.Write([]byte(out))
218- return nil
219- }
220-
221- for _, project := range projects {
222- out := fmt.Sprintf("%s (links to: %s)\n", project.Name, project.ProjectDir)
223- if project.Name == project.ProjectDir {
224- out = fmt.Sprintf("%s\n", project.Name)
225- }
226- _, _ = c.session.Write([]byte(out))
227- }
228-
229- return nil
230-}
231-
232-// delete all the projects and associated assets matching prefix
233-// but keep the latest N records
234-func (c *Cmd) prune(prefix string, keepNumLatest int) error {
235- c.log.Infof("user (%s) running `clean` command for (%s)", c.user.Name, prefix)
236- if prefix == "" || prefix == "*" {
237- e := fmt.Errorf("must provide valid prefix")
238- return e
239- }
240-
241- projects, err := c.dbpool.FindProjectsByPrefix(c.user.ID, prefix)
242- if err != nil {
243- return err
244- }
245-
246- rmProjects := []*db.Project{}
247- for _, project := range projects {
248- links, err := c.dbpool.FindProjectLinks(c.user.ID, project.Name)
249- if err != nil {
250- return err
251- }
252-
253- if len(links) == 0 {
254- out := fmt.Sprintf("project (%s) is available to delete\n", project.Name)
255- _, _ = c.session.Write([]byte(out))
256- rmProjects = append(rmProjects, project)
257- }
258- }
259-
260- goodbye := rmProjects
261- if keepNumLatest > 0 {
262- goodbye = rmProjects[:len(rmProjects)-keepNumLatest]
263- }
264-
265- for _, project := range goodbye {
266- err = c.rmProjectAssets(project.Name)
267- if err != nil {
268- return err
269- }
270-
271- out := fmt.Sprintf("(%s) removing ...\n", project.Name)
272- _, _ = c.session.Write([]byte(out))
273-
274- if c.write {
275- c.log.Infof("(%s) removing ...", project.Name)
276- err = c.dbpool.RemoveProject(project.ID)
277- if err != nil {
278- return err
279- }
280- }
281- }
282-
283- return nil
284-}
285-
286-func (c *Cmd) rm(projectName string) error {
287- c.log.Infof("user (%s) running `rm` command for (%s)", c.user.Name, projectName)
288- project, err := c.dbpool.FindProjectByName(c.user.ID, projectName)
289- if err == nil {
290- c.log.Infof("found project (%s) (%s), checking dependencies ...", projectName, project.ID)
291-
292- links, err := c.dbpool.FindProjectLinks(c.user.ID, projectName)
293- if err != nil {
294- return err
295- }
296-
297- if len(links) > 0 {
298- e := fmt.Errorf("project (%s) has (%d) other projects linking to it, cannot delete project until they have been unlinked or removed, aborting ...", projectName, len(links))
299- return e
300- }
301-
302- out := fmt.Sprintf("(%s) removing ...\n", project.Name)
303- _, _ = c.session.Write([]byte(out))
304- if c.write {
305- c.log.Infof("(%s) removing ...", project.Name)
306- err = c.dbpool.RemoveProject(project.ID)
307- if err != nil {
308- return err
309- }
310- }
311- } else {
312- e := fmt.Errorf("(%s) project not found for user (%s)", projectName, c.user.Name)
313- return e
314- }
315-
316- err = c.rmProjectAssets(project.Name)
317- return err
318-}
319-
320-func (c *Cmd) bail(err error) {
321- if err == nil {
322- return
323- }
324- c.log.Error(err)
325- utils.ErrorHandler(c.session, err)
326-}
327-
328-func (c *Cmd) notice() {
329- if !c.write {
330- out := fmt.Sprintf("\nNOTICE: changes not commited, use `--write` to save operation\n")
331- _, _ = c.session.Write([]byte(out))
332- }
333-}
334-
335 func WishMiddleware(handler *uploadassets.UploadAssetHandler) wish.Middleware {
336 dbpool := handler.DBPool
337 log := handler.Cfg.Logger