- commit
- a7bc7f3
- parent
- 8c8fe2b
- author
- Eric Bower
- date
- 2023-08-17 04:03:48 +0000 UTC
refactor(pgs): reorganized ssh cmds feat: all cmds are safe by default, use `--write` to commit
3 files changed,
+360,
-269
M
db/db.go
+1,
-1
1@@ -207,7 +207,7 @@ type DB interface {
2 FindFeedItemsByPostID(postID string) ([]*FeedItem, error)
3
4 InsertProject(userID, name, projectDir string) (string, error)
5- LinkToProject(userID, projectID, projectDir string) error
6+ LinkToProject(userID, projectID, projectDir string, commit bool) error
7 RemoveProject(projectID string) error
8 FindProjectByName(userID, name string) (*Project, error)
9 FindProjectLinks(userID, name string) ([]*Project, error)
+10,
-8
1@@ -247,7 +247,7 @@ const (
2 sqlInsertProject = `INSERT INTO projects (user_id, name, project_dir) VALUES ($1, $2, $3) RETURNING id;`
3 sqlFindProjectByName = `SELECT id, user_id, name, project_dir FROM projects WHERE user_id = $1 AND name = $2;`
4 sqlFindProjectsByUser = `SELECT id, user_id, name, project_dir FROM projects WHERE user_id = $1 ORDER BY name ASC;`
5- sqlFindProjectsByPrefix = `SELECT id, user_id, name, project_dir FROM projects WHERE user_id = $1 AND name = project_dir AND name ILIKE $2 ORDER BY name ASC;`
6+ sqlFindProjectsByPrefix = `SELECT id, user_id, name, project_dir FROM projects WHERE user_id = $1 AND name = project_dir AND name ILIKE $2 ORDER BY updated_at ASC, name ASC;`
7 sqlFindProjectLinks = `SELECT id, user_id, name, project_dir FROM projects WHERE user_id = $1 AND name != project_dir AND project_dir = $2 ORDER BY name ASC;`
8 sqlLinkToProject = `UPDATE projects SET project_dir = $1, updated_at = $2 WHERE id = $3;`
9 sqlRemoveProject = `DELETE FROM projects WHERE id = $1;`
10@@ -1196,7 +1196,7 @@ func (me *PsqlDB) InsertProject(userID, name, projectDir string) (string, error)
11 return id, nil
12 }
13
14-func (me *PsqlDB) LinkToProject(userID, projectID, projectDir string) error {
15+func (me *PsqlDB) LinkToProject(userID, projectID, projectDir string, commit bool) error {
16 linkToProject, err := me.FindProjectByName(userID, projectDir)
17 if err != nil {
18 return err
19@@ -1229,12 +1229,14 @@ func (me *PsqlDB) LinkToProject(userID, projectID, projectDir string) error {
20 )
21 }
22
23- _, err = me.Db.Exec(
24- sqlLinkToProject,
25- projectDir,
26- time.Now(),
27- projectID,
28- )
29+ if commit {
30+ _, err = me.Db.Exec(
31+ sqlLinkToProject,
32+ projectDir,
33+ time.Now(),
34+ projectID,
35+ )
36+ }
37 return err
38 }
39
+349,
-260
1@@ -1,6 +1,7 @@
2 package pgs
3
4 import (
5+ "errors"
6 "fmt"
7 "strings"
8
9@@ -12,6 +13,7 @@ import (
10 "github.com/picosh/pico/shared/storage"
11 "github.com/picosh/pico/wish/cms/util"
12 "github.com/picosh/pico/wish/send/utils"
13+ "go.uber.org/zap"
14 )
15
16 func getUser(s ssh.Session, dbpool db.DB) (*db.User, error) {
17@@ -38,44 +40,309 @@ type ProjectDetails struct {
18 store storage.ObjectStorage
19 }
20
21-func (pd *ProjectDetails) rmProjectAssets(userID string, projectName string) error {
22- bucketName := shared.GetAssetBucketName(userID)
23- bucket, err := pd.store.GetBucket(bucketName)
24+func getHelpText(userName, projectName string) string {
25+ helpStr := "commands: [help, stats, ls, rm, link, unlink, prune, retain, depends]\n\n"
26+ sshCmdStr := fmt.Sprintf("ssh %s@pgs.sh", userName)
27+ helpStr += fmt.Sprintf("`%s help`: prints this screen\n", sshCmdStr)
28+ helpStr += fmt.Sprintf("`%s stats`: prints stats for user\n", sshCmdStr)
29+ helpStr += fmt.Sprintf("`%s ls`: lists projects\n", sshCmdStr)
30+ helpStr += fmt.Sprintf("`%s rm %s`: deletes `%s`\n", sshCmdStr, projectName, projectName)
31+ helpStr += fmt.Sprintf("`%s link %s project-b`: symbolic link from `%s` to `project-b`\n", sshCmdStr, projectName, projectName)
32+ helpStr += fmt.Sprintf(
33+ "`%s unlink %s`: alias for `link %s %s`, which removes symbolic link for `%s`\n",
34+ sshCmdStr, projectName, projectName, projectName, projectName,
35+ )
36+ helpStr += fmt.Sprintf("`%s prune %s`: removes all projects that match prefix `%s` and is not linked to another project\n", sshCmdStr, projectName, projectName)
37+ helpStr += fmt.Sprintf("`%s retain %s`: alias for `prune` but retains the (3) most recently updated projects\n", sshCmdStr, projectName)
38+ helpStr += fmt.Sprintf("`%s depends %s`: lists all projects linked to `%s`\n", sshCmdStr, projectName, projectName)
39+ return helpStr
40+}
41+
42+type Cmd struct {
43+ user *db.User
44+ session ssh.Session
45+ log *zap.SugaredLogger
46+ store storage.ObjectStorage
47+ dbpool db.DB
48+ write bool
49+}
50+
51+func (c *Cmd) rmProjectAssets(projectName string) error {
52+ bucketName := shared.GetAssetBucketName(c.user.ID)
53+ bucket, err := c.store.GetBucket(bucketName)
54 if err != nil {
55 return err
56 }
57
58- fileList, err := pd.store.ListFiles(bucket, projectName+"/", true)
59+ fileList, err := c.store.ListFiles(bucket, projectName+"/", true)
60 if err != nil {
61 return err
62 }
63
64 for _, file := range fileList {
65- err = pd.store.DeleteFile(bucket, file.Name())
66- if err == nil {
67- _, _ = pd.session.Write([]byte(fmt.Sprintf("deleted (%s)\n", file.Name())))
68+ intent := []byte(fmt.Sprintf("deleted (%s)\n", file.Name()))
69+ if c.write {
70+ err = c.store.DeleteFile(bucket, file.Name())
71+ if err == nil {
72+ _, _ = c.session.Write(intent)
73+ } else {
74+ return err
75+ }
76 } else {
77- return err
78+ _, _ = c.session.Write(intent)
79 }
80 }
81 return nil
82 }
83
84-func getHelpText(userName, projectName string) string {
85- helpStr := "commands: [help, stats, ls, rm, clean, link, links, unlink]\n\n"
86- sshCmdStr := fmt.Sprintf("ssh %s@pgs.sh", userName)
87- helpStr += fmt.Sprintf("`%s help`: prints this screen\n", sshCmdStr)
88- helpStr += fmt.Sprintf("`%s stats`: prints stats for user\n", sshCmdStr)
89- helpStr += fmt.Sprintf("`%s ls`: lists projects\n", sshCmdStr)
90- helpStr += fmt.Sprintf("`%s %s rm`: deletes `%s`\n", sshCmdStr, projectName, projectName)
91- helpStr += fmt.Sprintf("`%s %s clean`: removes all projects that match prefix `%s` and is not linked to another project\n", sshCmdStr, projectName, projectName)
92- helpStr += fmt.Sprintf("`%s %s links`: lists all projects linked to `%s`\n", sshCmdStr, projectName, projectName)
93- helpStr += fmt.Sprintf("`%s %s link project-b`: symbolic link from `%s` to `project-b`\n", sshCmdStr, projectName, projectName)
94- helpStr += fmt.Sprintf(
95- "`%s %s unlink`: alias for `%s link %s`, which removes symbolic link for `%s`\n",
96- sshCmdStr, projectName, projectName, projectName, projectName,
97+func (c *Cmd) help() {
98+ _, _ = c.session.Write([]byte(getHelpText(c.user.Name, "project-a")))
99+}
100+
101+func (c *Cmd) stats(maxSize int) error {
102+ bucketName := shared.GetAssetBucketName(c.user.ID)
103+ bucket, err := c.store.UpsertBucket(bucketName)
104+ if err != nil {
105+ return err
106+ }
107+
108+ totalFileSize, err := c.store.GetBucketQuota(bucket)
109+ if err != nil {
110+ return err
111+ }
112+
113+ projects, err := c.dbpool.FindProjectsByUser(c.user.ID)
114+ if err != nil {
115+ return err
116+ }
117+
118+ str := "stats\n"
119+ str += "=====\n"
120+ str += fmt.Sprintf(
121+ "space:\t\t%.4f/%.4fGB, %.4f%%\n",
122+ shared.BytesToGB(int(totalFileSize)),
123+ shared.BytesToGB(maxSize),
124+ (float32(totalFileSize)/float32(maxSize))*100,
125 )
126- return helpStr
127+ str += fmt.Sprintf("projects:\t%d\n", len(projects))
128+ _, _ = c.session.Write([]byte(str))
129+
130+ return nil
131+}
132+
133+func (c *Cmd) ls() error {
134+ projects, err := c.dbpool.FindProjectsByUser(c.user.ID)
135+ if err != nil {
136+ return err
137+ }
138+
139+ if len(projects) == 0 {
140+ out := "no linked projects found\n"
141+ _, _ = c.session.Write([]byte(out))
142+ }
143+
144+ for _, project := range projects {
145+ out := fmt.Sprintf("%s (links to: %s)\n", project.Name, project.ProjectDir)
146+ if project.Name == project.ProjectDir {
147+ out = fmt.Sprintf("%s\n", project.Name)
148+ }
149+ _, _ = c.session.Write([]byte(out))
150+ }
151+
152+ return nil
153+}
154+
155+func (c *Cmd) unlink(projectName string) error {
156+ c.log.Infof("user (%s) running `unlink` command with (%s)", c.user.Name, projectName)
157+ project, err := c.dbpool.FindProjectByName(c.user.ID, projectName)
158+ if err != nil {
159+ return errors.Join(err, fmt.Errorf("project (%s) does not exit", projectName))
160+ }
161+
162+ err = c.dbpool.LinkToProject(c.user.ID, project.ID, project.Name, c.write)
163+ if err != nil {
164+ return err
165+ }
166+
167+ return nil
168+}
169+
170+func (c *Cmd) link(projectName, linkTo string) error {
171+ c.log.Infof("user (%s) running `link` command with (%s) (%s)", c.user.Name, projectName, linkTo)
172+
173+ projectDir := linkTo
174+ _, err := c.dbpool.FindProjectByName(c.user.ID, linkTo)
175+ if err != nil {
176+ e := fmt.Errorf("(%s) project doesn't exist", linkTo)
177+ return e
178+ }
179+
180+ project, err := c.dbpool.FindProjectByName(c.user.ID, projectName)
181+ projectID := ""
182+ if err == nil {
183+ projectID = project.ID
184+ c.log.Infof("user (%s) already has project (%s), updating ...", c.user.Name, projectName)
185+ err = c.dbpool.LinkToProject(c.user.ID, project.ID, projectDir, c.write)
186+ if err != nil {
187+ return err
188+ }
189+ } else {
190+ c.log.Infof("user (%s) has no project record (%s), creating ...", c.user.Name, projectName)
191+ if !c.write {
192+ out := fmt.Sprintf("(%s) cannot create a new project without `--write` permission, aborting ...\n", projectName)
193+ _, _ = c.session.Write([]byte(out))
194+ return nil
195+ }
196+ id, err := c.dbpool.InsertProject(c.user.ID, projectName, projectName)
197+ if err != nil {
198+ return err
199+ }
200+ projectID = id
201+ }
202+
203+ c.log.Infof("user (%s) linking (%s) to (%s) ...", c.user.Name, projectName, projectDir)
204+ err = c.dbpool.LinkToProject(c.user.ID, projectID, projectDir, c.write)
205+ if err != nil {
206+ return err
207+ }
208+
209+ out := fmt.Sprintf("(%s) might have orphaned assets, removing ...\n", projectName)
210+ _, _ = c.session.Write([]byte(out))
211+
212+ err = c.rmProjectAssets(projectName)
213+ if err != nil {
214+ return err
215+ }
216+
217+ out = fmt.Sprintf("(%s) now points to (%s)\n", projectName, linkTo)
218+ _, _ = c.session.Write([]byte(out))
219+ return nil
220+}
221+
222+func (c *Cmd) depends(projectName string) error {
223+ projects, err := c.dbpool.FindProjectLinks(c.user.ID, projectName)
224+ if err != nil {
225+ return err
226+ }
227+
228+ if len(projects) == 0 {
229+ out := fmt.Sprintf("no projects linked to this project (%s) found\n", projectName)
230+ _, _ = c.session.Write([]byte(out))
231+ return nil
232+ }
233+
234+ for _, project := range projects {
235+ out := fmt.Sprintf("%s (links to: %s)\n", project.Name, project.ProjectDir)
236+ if project.Name == project.ProjectDir {
237+ out = fmt.Sprintf("%s\n", project.Name)
238+ }
239+ _, _ = c.session.Write([]byte(out))
240+ }
241+
242+ return nil
243+}
244+
245+// delete all the projects and associated assets matching prefix
246+// but keep the latest N records
247+func (c *Cmd) prune(prefix string, keepNumLatest int) error {
248+ c.log.Infof("user (%s) running `clean` command for (%s)", c.user.Name, prefix)
249+ if prefix == "" || prefix == "*" {
250+ e := fmt.Errorf("must provide valid prefix")
251+ return e
252+ }
253+
254+ projects, err := c.dbpool.FindProjectsByPrefix(c.user.ID, prefix)
255+ if err != nil {
256+ return err
257+ }
258+
259+ rmProjects := []*db.Project{}
260+ for _, project := range projects {
261+ links, err := c.dbpool.FindProjectLinks(c.user.ID, project.Name)
262+ if err != nil {
263+ return err
264+ }
265+
266+ if len(links) == 0 {
267+ out := fmt.Sprintf("project (%s) is available to delete\n", project.Name)
268+ _, _ = c.session.Write([]byte(out))
269+ rmProjects = append(rmProjects, project)
270+ }
271+ }
272+
273+ goodbye := rmProjects
274+ if keepNumLatest > 0 {
275+ goodbye = rmProjects[:len(rmProjects)-keepNumLatest]
276+ }
277+
278+ for _, project := range goodbye {
279+ err = c.rmProjectAssets(project.Name)
280+ if err != nil {
281+ return err
282+ }
283+
284+ out := fmt.Sprintf("(%s) removing ...\n", project.Name)
285+ _, _ = c.session.Write([]byte(out))
286+
287+ if c.write {
288+ c.log.Infof("(%s) removing ...", project.Name)
289+ err = c.dbpool.RemoveProject(project.ID)
290+ if err != nil {
291+ return err
292+ }
293+ }
294+ }
295+
296+ return nil
297+}
298+
299+func (c *Cmd) rm(projectName string) error {
300+ c.log.Infof("user (%s) running `rm` command for (%s)", c.user.Name, projectName)
301+ project, err := c.dbpool.FindProjectByName(c.user.ID, projectName)
302+ if err == nil {
303+ c.log.Infof("found project (%s) (%s), checking dependencies ...", projectName, project.ID)
304+
305+ links, err := c.dbpool.FindProjectLinks(c.user.ID, projectName)
306+ if err != nil {
307+ return err
308+ }
309+
310+ if len(links) > 0 {
311+ 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))
312+ return e
313+ }
314+
315+ out := fmt.Sprintf("(%s) removing ...\n", project.Name)
316+ _, _ = c.session.Write([]byte(out))
317+ if c.write {
318+ c.log.Infof("(%s) removing ...", project.Name)
319+ err = c.dbpool.RemoveProject(project.ID)
320+ if err != nil {
321+ return err
322+ }
323+ }
324+ } else {
325+ e := fmt.Errorf("(%s) project not found for user (%s)", projectName, c.user.Name)
326+ return e
327+ }
328+
329+ err = c.rmProjectAssets(project.Name)
330+ return err
331+}
332+
333+func (c *Cmd) bail(err error) {
334+ if err == nil {
335+ return
336+ }
337+ c.log.Error(err)
338+ utils.ErrorHandler(c.session, err)
339+}
340+
341+func (c *Cmd) notice() {
342+ if !c.write {
343+ out := fmt.Sprintf("\nNOTICE: changes not commited, use `--write` to save operation\n")
344+ _, _ = c.session.Write([]byte(out))
345+ }
346 }
347
348 func WishMiddleware(handler *uploadassets.UploadAssetHandler) wish.Middleware {
349@@ -100,271 +367,93 @@ func WishMiddleware(handler *uploadassets.UploadAssetHandler) wish.Middleware {
350 }
351
352 args := session.Command()
353+
354+ opts := Cmd{
355+ session: session,
356+ user: user,
357+ store: store,
358+ log: log,
359+ dbpool: dbpool,
360+ write: false,
361+ }
362+
363+ cmd := strings.TrimSpace(args[0])
364 if len(args) == 1 {
365- cmd := strings.TrimSpace(args[0])
366 if cmd == "help" {
367- _, _ = session.Write([]byte(getHelpText(user.Name, "project-a")))
368+ opts.help()
369+ return
370 } else if cmd == "stats" {
371- bucketName := shared.GetAssetBucketName(user.ID)
372- bucket, err := store.UpsertBucket(bucketName)
373- if err != nil {
374- log.Error(err)
375- utils.ErrorHandler(session, err)
376- return
377- }
378-
379- totalFileSize, err := store.GetBucketQuota(bucket)
380- if err != nil {
381- log.Error(err)
382- utils.ErrorHandler(session, err)
383- return
384- }
385-
386- projects, err := dbpool.FindProjectsByUser(user.ID)
387- if err != nil {
388- log.Error(err)
389- utils.ErrorHandler(session, err)
390- return
391- }
392-
393- str := "stats\n"
394- str += "=====\n"
395- str += fmt.Sprintf(
396- "space:\t\t%.4f/%.4fGB, %.4f%%\n",
397- shared.BytesToGB(int(totalFileSize)),
398- shared.BytesToGB(cfg.MaxSize),
399- (float32(totalFileSize)/float32(cfg.MaxSize))*100,
400- )
401- str += fmt.Sprintf("projects:\t%d\n", len(projects))
402- _, _ = session.Write([]byte(str))
403+ err := opts.stats(cfg.MaxSize)
404+ opts.bail(err)
405+ return
406+ } else if cmd == "ls" {
407+ err := opts.ls()
408+ opts.bail(err)
409+ return
410+ } else {
411+ e := fmt.Errorf("%s not a valid command", args)
412+ opts.bail(e)
413 return
414- } else if cmd == "list" || cmd == "ls" {
415- projects, err := dbpool.FindProjectsByUser(user.ID)
416- if err != nil {
417- log.Error(err)
418- utils.ErrorHandler(session, err)
419- return
420- }
421-
422- if len(projects) == 0 {
423- out := "no linked projects found\n"
424- _, _ = session.Write([]byte(out))
425- }
426-
427- for _, project := range projects {
428- out := fmt.Sprintf("%s (links to: %s)\n", project.Name, project.ProjectDir)
429- if project.Name == project.ProjectDir {
430- out = fmt.Sprintf("%s\n", project.Name)
431- }
432- _, _ = session.Write([]byte(out))
433- }
434 }
435- return
436- } else if len(args) < 2 {
437- utils.ErrorHandler(session, fmt.Errorf("must supply project name and then a command"))
438- return
439 }
440
441- projectName := strings.TrimSpace(args[0])
442- cmd := strings.TrimSpace(args[1])
443 log.Infof("pgs middleware detected command: %s", args)
444+ projectName := strings.TrimSpace(args[1])
445
446- if cmd == "help" {
447- log.Infof("user (%s) running `help` command", user.Name)
448- _, _ = session.Write([]byte(getHelpText(user.Name, projectName)))
449+ if projectName == "--write" {
450+ utils.ErrorHandler(session, fmt.Errorf("`--write` should be placed at end of command"))
451 return
452- } else if cmd == "unlink" {
453- log.Infof("user (%s) running `unlink` command with (%s)", user.Name, projectName)
454- project, err := dbpool.FindProjectByName(user.ID, projectName)
455- if err != nil {
456- log.Error(err)
457- utils.ErrorHandler(session, fmt.Errorf("project (%s) does not exit", projectName))
458- return
459- }
460- err = dbpool.LinkToProject(user.ID, project.ID, project.Name)
461- if err != nil {
462- log.Error(err)
463- utils.ErrorHandler(session, err)
464- return
465- }
466+ }
467
468- return
469- } else if cmd == "link" {
470+ if cmd == "link" {
471 if len(args) < 3 {
472 utils.ErrorHandler(session, fmt.Errorf("must supply link command like: `projectA link projectB`"))
473 return
474 }
475 linkTo := strings.TrimSpace(args[2])
476- log.Infof("user (%s) running `link` command with (%s) (%s)", user.Name, projectName, linkTo)
477-
478- projectDir := linkTo
479- _, err := dbpool.FindProjectByName(user.ID, linkTo)
480- if err != nil {
481- e := fmt.Errorf("(%s) project doesn't exist", linkTo)
482- log.Error(e)
483- utils.ErrorHandler(session, e)
484- return
485+ if len(args) >= 4 && strings.TrimSpace(args[3]) == "--write" {
486+ opts.write = true
487 }
488
489- project, err := dbpool.FindProjectByName(user.ID, projectName)
490- projectID := ""
491- if err == nil {
492- projectID = project.ID
493- log.Infof("user (%s) already has project (%s), updating ...", user.Name, projectName)
494- err = dbpool.LinkToProject(user.ID, project.ID, projectDir)
495- if err != nil {
496- log.Error(err)
497- utils.ErrorHandler(session, err)
498- return
499- }
500- } else {
501- log.Infof("user (%s) has no project record (%s), creating ...", user.Name, projectName)
502- id, err := dbpool.InsertProject(user.ID, projectName, projectName)
503- if err != nil {
504- log.Error(err)
505- utils.ErrorHandler(session, err)
506- return
507- }
508- projectID = id
509- }
510-
511- log.Infof("user (%s) linking (%s) to (%s) ...", user.Name, projectName, projectDir)
512- err = dbpool.LinkToProject(user.ID, projectID, projectDir)
513- if err != nil {
514- log.Error(err)
515- utils.ErrorHandler(session, err)
516- return
517- }
518-
519- out := fmt.Sprintf("(%s) might have orphaned assets, removing ...\n", projectName)
520- _, _ = session.Write([]byte(out))
521-
522- pd := ProjectDetails{
523- session: session,
524- store: store,
525- }
526- err = pd.rmProjectAssets(user.ID, projectName)
527+ err := opts.link(projectName, linkTo)
528+ opts.notice()
529 if err != nil {
530- log.Error(err)
531- utils.ErrorHandler(session, err)
532+ opts.bail(err)
533 }
534-
535- out = fmt.Sprintf("(%s) now points to (%s)\n", projectName, linkTo)
536- _, _ = session.Write([]byte(out))
537 return
538- } else if cmd == "links" {
539- projects, err := dbpool.FindProjectLinks(user.ID, projectName)
540- if err != nil {
541- log.Error(err)
542- utils.ErrorHandler(session, err)
543- return
544- }
545-
546- if len(projects) == 0 {
547- out := fmt.Sprintf("no projects linked to this project (%s) found\n", projectName)
548- _, _ = session.Write([]byte(out))
549- return
550- }
551-
552- for _, project := range projects {
553- out := fmt.Sprintf("%s (links to: %s)\n", project.Name, project.ProjectDir)
554- if project.Name == project.ProjectDir {
555- out = fmt.Sprintf("%s\n", project.Name)
556- }
557- _, _ = session.Write([]byte(out))
558- }
559- } else if cmd == "clean" {
560- log.Infof("user (%s) running `clean` command for (%s)", user.Name, projectName)
561- if projectName == "" || projectName == "*" {
562- e := fmt.Errorf("must provide valid prefix")
563- log.Error(e)
564- utils.ErrorHandler(session, e)
565- return
566- }
567-
568- projects, err := dbpool.FindProjectsByPrefix(user.ID, projectName)
569- if err != nil {
570- log.Error(err)
571- utils.ErrorHandler(session, err)
572- }
573+ }
574
575- rmProjects := []*db.Project{}
576- for _, project := range projects {
577- links, err := dbpool.FindProjectLinks(user.ID, project.Name)
578- if err != nil {
579- log.Error(err)
580- utils.ErrorHandler(session, err)
581- }
582-
583- if len(links) == 0 {
584- out := fmt.Sprintf("project (%s) is available to delete\n", project.Name)
585- _, _ = session.Write([]byte(out))
586- rmProjects = append(rmProjects, project)
587- }
588- }
589+ if len(args) >= 3 && strings.TrimSpace(args[2]) == "--write" {
590+ opts.write = true
591+ }
592
593- for _, project := range rmProjects {
594- pd := ProjectDetails{
595- session: session,
596- store: store,
597- }
598- err = pd.rmProjectAssets(user.ID, project.Name)
599- if err != nil {
600- log.Error(err)
601- utils.ErrorHandler(session, err)
602- }
603-
604- err = dbpool.RemoveProject(project.ID)
605- if err != nil {
606- log.Error(err)
607- utils.ErrorHandler(session, err)
608- }
609- }
610+ if cmd == "unlink" {
611+ err := opts.unlink(projectName)
612+ opts.notice()
613+ opts.bail(err)
614+ return
615+ } else if cmd == "depends" {
616+ err := opts.depends(projectName)
617+ opts.bail(err)
618+ return
619+ } else if cmd == "retain" {
620+ err := opts.prune(projectName, 3)
621+ opts.notice()
622+ opts.bail(err)
623+ return
624+ } else if cmd == "prune" {
625+ err := opts.prune(projectName, 0)
626+ opts.notice()
627+ opts.bail(err)
628+ return
629 } else if cmd == "rm" {
630- log.Infof("user (%s) running `rm` command for (%s)", user.Name, projectName)
631- project, err := dbpool.FindProjectByName(user.ID, projectName)
632- if err == nil {
633- log.Infof("found project (%s) (%s), checking dependencies ...", projectName, project.ID)
634-
635- links, err := dbpool.FindProjectLinks(user.ID, projectName)
636- if err != nil {
637- log.Error(err)
638- utils.ErrorHandler(session, err)
639- }
640-
641- if len(links) > 0 {
642- e := fmt.Errorf("project (%s) has (%d) other projects linking to it, can't delete project until they have been unlinked or removed, aborting ...", projectName, len(links))
643- log.Error(e)
644- return
645- }
646-
647- err = dbpool.RemoveProject(project.ID)
648- if err != nil {
649- log.Error(err)
650- utils.ErrorHandler(session, err)
651- }
652- } else {
653- e := fmt.Errorf("(%s) project not found for user (%s)", projectName, user.Name)
654- log.Error(e)
655- utils.ErrorHandler(session, e)
656- return
657- }
658-
659- pd := ProjectDetails{
660- session: session,
661- store: store,
662- }
663- err = pd.rmProjectAssets(user.ID, project.Name)
664- if err != nil {
665- log.Error(err)
666- utils.ErrorHandler(session, err)
667- }
668-
669+ err := opts.rm(projectName)
670+ opts.notice()
671+ opts.bail(err)
672 return
673 } else {
674 e := fmt.Errorf("%s not a valid command", args)
675- log.Error(e)
676- utils.ErrorHandler(session, e)
677+ opts.bail(e)
678 return
679 }
680 }