- commit
- 1f65f3f
- parent
- e94d326
- author
- Eric Bower
- date
- 2024-02-28 03:31:39 +0000 UTC
refactor(pgs): new cli api
3 files changed,
+120,
-51
+1,
-1
1@@ -32,7 +32,7 @@ func HasProjectAccess(project *db.Project, owner *db.User, requester *db.User, p
2 return slices.Contains(data, requester.Name)
3 }
4
5- if aclType == "public_keys" {
6+ if aclType == "pubkeys" {
7 key := ssh.FingerprintSHA256(pubkey)
8 if len(data) == 0 {
9 return true
+6,
-5
1@@ -69,10 +69,11 @@ func projectTable(projects []*db.Project) *table.Table {
2 return t
3 }
4
5-func getHelpText(userName, projectName string) string {
6+func getHelpText(userName string) string {
7 helpStr := "Commands: [help, stats, ls, rm, link, unlink, prune, retain, depends, acl]\n\n"
8 helpStr += "NOTICE: *must* append with `--write` for the changes to persist.\n\n"
9
10+ projectName := "projA"
11 headers := []string{"Cmd", "Description"}
12 data := [][]string{
13 {
14@@ -92,8 +93,8 @@ func getHelpText(userName, projectName string) string {
15 fmt.Sprintf("delete %s", projectName),
16 },
17 {
18- fmt.Sprintf("link %s project-b", projectName),
19- fmt.Sprintf("symbolic link `%s` to `project-b`", projectName),
20+ fmt.Sprintf("link %s --to projB", projectName),
21+ fmt.Sprintf("symbolic link `%s` to `projB`", projectName),
22 },
23 {
24 fmt.Sprintf("unlink %s", projectName),
25@@ -105,7 +106,7 @@ func getHelpText(userName, projectName string) string {
26 },
27 {
28 fmt.Sprintf("retain %s", projectName),
29- "alias `prune` but keeps last (3) projects",
30+ "alias to `prune` but keeps last N projects",
31 },
32 {
33 fmt.Sprintf("depends %s", projectName),
34@@ -235,7 +236,7 @@ func (c *Cmd) RmProjectAssets(projectName string) error {
35 }
36
37 func (c *Cmd) help() {
38- c.output(getHelpText(c.User.Name, "project-a"))
39+ c.output(getHelpText(c.User.Name))
40 }
41
42 func (c *Cmd) stats(cfgMaxSize uint64) error {
+113,
-45
1@@ -1,6 +1,7 @@
2 package pgs
3
4 import (
5+ "flag"
6 "fmt"
7 "slices"
8 "strings"
9@@ -32,6 +33,35 @@ func getUser(s ssh.Session, dbpool db.DB) (*db.User, error) {
10 return user, nil
11 }
12
13+type arrayFlags []string
14+
15+func (i *arrayFlags) String() string {
16+ return "array flags"
17+}
18+
19+func (i *arrayFlags) Set(value string) error {
20+ *i = append(*i, value)
21+ return nil
22+}
23+
24+func flagSet(cmdName string, sesh ssh.Session) (*flag.FlagSet, bool) {
25+ cmd := flag.NewFlagSet(cmdName, flag.ContinueOnError)
26+ cmd.SetOutput(sesh)
27+ write := cmd.Bool("write", false, "apply changes")
28+ return cmd, *write
29+}
30+
31+func flagCheck(cmd *flag.FlagSet, posArg string, cmdArgs []string) bool {
32+ cmd.Parse(cmdArgs)
33+
34+ if posArg == "-h" || posArg == "--help" || posArg == "-help" {
35+ cmd.Usage()
36+ return false
37+ }
38+ return true
39+}
40+
41+
42 func WishMiddleware(handler *uploadassets.UploadAssetHandler) wish.Middleware {
43 dbpool := handler.DBPool
44 log := handler.Cfg.Logger
45@@ -82,37 +112,51 @@ func WishMiddleware(handler *uploadassets.UploadAssetHandler) wish.Middleware {
46 }
47 }
48
49- log.Info("pgs middleware detected command", "args", args)
50 projectName := strings.TrimSpace(args[1])
51-
52- if projectName == "--write" {
53- utils.ErrorHandler(sesh, fmt.Errorf("`--write` should be placed at end of command"))
54- return
55- }
56+ cmdArgs := args[2:]
57+ log.Info(
58+ "pgs middleware detected command",
59+ "args", args,
60+ "cmd", cmd,
61+ "projectName", projectName,
62+ "cmdArgs", cmdArgs,
63+ )
64
65 if cmd == "link" {
66- if len(args) < 3 {
67- utils.ErrorHandler(sesh, fmt.Errorf("must supply link command like: `projectA link projectB`"))
68- return
69- }
70- linkTo := strings.TrimSpace(args[2])
71- if len(args) >= 4 && strings.TrimSpace(args[3]) == "--write" {
72+ linkCmd, write := flagSet("link", sesh)
73+ linkTo := linkCmd.String("to", "", "symbolic link to this project")
74+ if !flagCheck(linkCmd, projectName, cmdArgs) {
75+ return
76+ }
77+
78+ if write == true {
79 opts.Write = true
80 }
81
82- err := opts.link(projectName, linkTo)
83+ if *linkTo == "" {
84+ err := fmt.Errorf(
85+ "must provide `--to` flag",
86+ )
87+ opts.bail(err)
88+ return
89+ }
90+
91+ err := opts.link(projectName, *linkTo)
92 opts.notice()
93 if err != nil {
94 opts.bail(err)
95 }
96 return
97- }
98+ } else if cmd == "unlink" {
99+ unlinkCmd, write := flagSet("unlink", sesh)
100+ if !flagCheck(unlinkCmd, projectName, cmdArgs) {
101+ return
102+ }
103
104- if len(args) >= 3 && strings.TrimSpace(args[2]) == "--write" {
105- opts.Write = true
106- }
107+ if write == true {
108+ opts.Write = true
109+ }
110
111- if cmd == "unlink" {
112 err := opts.unlink(projectName)
113 opts.notice()
114 opts.bail(err)
115@@ -122,51 +166,75 @@ func WishMiddleware(handler *uploadassets.UploadAssetHandler) wish.Middleware {
116 opts.bail(err)
117 return
118 } else if cmd == "retain" {
119- err := opts.prune(projectName, 3)
120+ retainCmd, write := flagSet("retain", sesh)
121+ retainNum := retainCmd.Int("n", 3, "latest number of projects to keep")
122+ if !flagCheck(retainCmd, projectName, cmdArgs) {
123+ return
124+ }
125+
126+ if write == true {
127+ opts.Write = true
128+ }
129+
130+ err := opts.prune(projectName, *retainNum)
131 opts.notice()
132 opts.bail(err)
133 return
134 } else if cmd == "prune" {
135+ pruneCmd, write := flagSet("prune", sesh)
136+ if !flagCheck(pruneCmd, projectName, cmdArgs) {
137+ return
138+ }
139+
140+ if write == true {
141+ opts.Write = true
142+ }
143+
144 err := opts.prune(projectName, 0)
145 opts.notice()
146 opts.bail(err)
147 return
148 } else if cmd == "rm" {
149+ rmCmd, write := flagSet("rm", sesh)
150+ if !flagCheck(rmCmd, projectName, cmdArgs) {
151+ return
152+ }
153+
154+ if write == true {
155+ opts.Write = true
156+ }
157+
158 err := opts.rm(projectName)
159 opts.notice()
160 opts.bail(err)
161 return
162 } else if cmd == "acl" {
163- aclType := strings.TrimSpace(args[2])
164- if !slices.Contains([]string{"public", "public_keys", "pico"}, aclType) {
165- err := fmt.Errorf("acl type must be one of the following: [public, public_keys, pico], found %s", aclType)
166+ aclCmd, write := flagSet("acl", sesh)
167+ aclType := aclCmd.String("type", "", "access type: public, pico, pubkeys")
168+ var acls arrayFlags
169+ aclCmd.Var(
170+ &acls,
171+ "acl",
172+ "list of pico usernames or sha256 public keys, delimited by commas",
173+ )
174+ if !flagCheck(aclCmd, projectName, cmdArgs) {
175+ return
176+ }
177+
178+ if write == true {
179+ opts.Write = true
180+ }
181+
182+ if !slices.Contains([]string{"public", "pubkeys", "pico"}, *aclType) {
183+ err := fmt.Errorf(
184+ "acl type must be one of the following: [public, pubkeys, pico], found %s",
185+ *aclType,
186+ )
187 opts.bail(err)
188 return
189 }
190
191- aclsRaw := ""
192- if len(args) == 4 {
193- if strings.TrimSpace(args[3]) == "--write" {
194- opts.Write = true
195- } else {
196- aclsRaw = strings.TrimSpace(args[3])
197- }
198- }
199- if len(args) == 5 {
200- aclsRaw = strings.TrimSpace(args[3])
201- if strings.TrimSpace(args[4]) == "--write" {
202- opts.Write = true
203- }
204- }
205- acls := []string{}
206- for _, key := range strings.Split(aclsRaw, ",") {
207- st := strings.TrimSpace(key)
208- if st == "" {
209- continue
210- }
211- acls = append(acls, st)
212- }
213- err := opts.acl(projectName, aclType, acls)
214+ err := opts.acl(projectName, *aclType, acls)
215 opts.notice()
216 opts.bail(err)
217 } else {