repos / pico

pico services - prose.sh, pastes.sh, imgs.sh, feeds.sh, pgs.sh
git clone https://github.com/picosh/pico.git

commit
327b26e
parent
1fc495d
author
Eric Bower
date
2023-08-17 19:41:36 +0000 UTC
refactor(pgs): cli outputs and asset upload optimizations
4 files changed,  +125, -63
M db/db.go
+1, -0
1@@ -207,6 +207,7 @@ type DB interface {
2 	FindFeedItemsByPostID(postID string) ([]*FeedItem, error)
3 
4 	InsertProject(userID, name, projectDir string) (string, error)
5+	UpdateProject(userID, name string) error
6 	LinkToProject(userID, projectID, projectDir string, commit bool) error
7 	RemoveProject(projectID string) error
8 	FindProjectByName(userID, name string) (*Project, error)
M db/postgres/storage.go
+6, -0
 1@@ -245,6 +245,7 @@ const (
 2 	sqlRemoveUsers         = `DELETE FROM app_users WHERE id = ANY($1::uuid[])`
 3 
 4 	sqlInsertProject        = `INSERT INTO projects (user_id, name, project_dir) VALUES ($1, $2, $3) RETURNING id;`
 5+	sqlUpdateProject        = `UPDATE projects SET updated_at = $3 WHERE user_id = $1 AND name = $2;`
 6 	sqlFindProjectByName    = `SELECT id, user_id, name, project_dir FROM projects WHERE user_id = $1 AND name = $2;`
 7 	sqlFindProjectsByUser   = `SELECT id, user_id, name, project_dir FROM projects WHERE user_id = $1 ORDER BY name ASC;`
 8 	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;`
 9@@ -1196,6 +1197,11 @@ func (me *PsqlDB) InsertProject(userID, name, projectDir string) (string, error)
10 	return id, nil
11 }
12 
13+func (me *PsqlDB) UpdateProject(userID, name string) error {
14+	_, err := me.Db.Exec(sqlUpdateProject, userID, name, time.Now())
15+	return err
16+}
17+
18 func (me *PsqlDB) LinkToProject(userID, projectID, projectDir string, commit bool) error {
19 	linkToProject, err := me.FindProjectByName(userID, projectDir)
20 	if err != nil {
M filehandlers/assets/handler.go
+46, -13
 1@@ -18,6 +18,8 @@ import (
 2 )
 3 
 4 type ctxUserKey struct{}
 5+type ctxBucketKey struct{}
 6+type ctxProjectKey struct{}
 7 
 8 func getAssetURL(c *shared.ConfigSite, username, projectName, fpath string) string {
 9 	return fmt.Sprintf(
10@@ -30,6 +32,23 @@ func getAssetURL(c *shared.ConfigSite, username, projectName, fpath string) stri
11 	)
12 }
13 
14+func getProject(s ssh.Session) *db.Project {
15+	v := s.Context().Value(ctxProjectKey{})
16+	if v == nil {
17+		return nil
18+	}
19+	project := s.Context().Value(ctxProjectKey{}).(*db.Project)
20+	return project
21+}
22+
23+func getBucket(s ssh.Session) (storage.Bucket, error) {
24+	bucket := s.Context().Value(ctxBucketKey{}).(storage.Bucket)
25+	if bucket.Name == "" {
26+		return bucket, fmt.Errorf("bucket not set on `ssh.Context()` for connection")
27+	}
28+	return bucket, nil
29+}
30+
31 func getUser(s ssh.Session) (*db.User, error) {
32 	user := s.Context().Value(ctxUserKey{}).(*db.User)
33 	if user == nil {
34@@ -145,6 +164,8 @@ func (h *UploadAssetHandler) Validate(s ssh.Session) error {
35 	if err != nil {
36 		return err
37 	}
38+	s.Context().SetValue(ctxBucketKey{}, bucket)
39+
40 	totalFileSize, err := h.Storage.GetBucketQuota(bucket)
41 	if err != nil {
42 		return err
43@@ -176,12 +197,35 @@ func (h *UploadAssetHandler) Write(s ssh.Session, entry *utils.FileEntry) (strin
44 	// filesize from sftp,scp,rsync
45 	entry.Size = int64(fileSize)
46 
47-	assetBucket := shared.GetAssetBucketName(user.ID)
48-	bucket, err := h.Storage.UpsertBucket(assetBucket)
49+	bucket, err := getBucket(s)
50 	if err != nil {
51 		return "", err
52 	}
53 
54+	hasProject := getProject(s)
55+	projectName := shared.GetProjectName(entry)
56+
57+	// find, create, or update project if we haven't already done it
58+	if hasProject == nil {
59+		project, err := h.DBPool.FindProjectByName(user.ID, projectName)
60+		if err == nil {
61+			err = h.DBPool.UpdateProject(user.ID, projectName)
62+			if err != nil {
63+				return "", err
64+			}
65+		} else {
66+			_, err = h.DBPool.InsertProject(user.ID, projectName, projectName)
67+			if err != nil {
68+				return "", err
69+			}
70+			project, err = h.DBPool.FindProjectByName(user.ID, projectName)
71+			if err != nil {
72+				return "", err
73+			}
74+		}
75+		s.Context().SetValue(ctxProjectKey{}, project)
76+	}
77+
78 	data := &FileData{
79 		FileEntry: entry,
80 		User:      user,
81@@ -193,17 +237,6 @@ func (h *UploadAssetHandler) Write(s ssh.Session, entry *utils.FileEntry) (strin
82 		return "", err
83 	}
84 
85-	projectName := shared.GetProjectName(entry)
86-
87-	// find and create project
88-	_, err = h.DBPool.FindProjectByName(user.ID, projectName)
89-	if err != nil {
90-		_, err = h.DBPool.InsertProject(user.ID, projectName, projectName)
91-		if err != nil {
92-			return "", err
93-		}
94-	}
95-
96 	url := getAssetURL(
97 		h.Cfg,
98 		user.Name,
M pgs/cli.go
+72, -50
  1@@ -14,6 +14,8 @@ import (
  2 
  3 func getHelpText(userName, projectName string) string {
  4 	helpStr := "commands: [help, stats, ls, rm, link, unlink, prune, retain, depends]\n\n"
  5+	helpStr += "NOTICE: any cmd that results in a mutation *must* be appended with `--write` for the changes to persist, otherwise it will simply output a dry-run.\n\n"
  6+
  7 	sshCmdStr := fmt.Sprintf("ssh %s@pgs.sh", userName)
  8 	helpStr += fmt.Sprintf("`%s help`: prints this screen\n", sshCmdStr)
  9 	helpStr += fmt.Sprintf("`%s stats`: prints stats for user\n", sshCmdStr)
 10@@ -39,36 +41,61 @@ type Cmd struct {
 11 	write   bool
 12 }
 13 
 14+func (c *Cmd) output(out string) {
 15+	_, _ = c.session.Write([]byte(out + "\n"))
 16+}
 17+
 18+func (c *Cmd) bail(err error) {
 19+	if err == nil {
 20+		return
 21+	}
 22+	c.log.Error(err)
 23+	utils.ErrorHandler(c.session, err)
 24+}
 25+
 26+func (c *Cmd) notice() {
 27+	if !c.write {
 28+		c.output("\nNOTICE: changes not commited, use `--write` to save operation")
 29+	}
 30+}
 31+
 32 func (c *Cmd) rmProjectAssets(projectName string) error {
 33 	bucketName := shared.GetAssetBucketName(c.user.ID)
 34 	bucket, err := c.store.GetBucket(bucketName)
 35 	if err != nil {
 36 		return err
 37 	}
 38+	c.output(fmt.Sprintf("removing project assets (%s)", projectName))
 39 
 40 	fileList, err := c.store.ListFiles(bucket, projectName+"/", true)
 41 	if err != nil {
 42 		return err
 43 	}
 44 
 45+	if len(fileList) == 0 {
 46+		c.output(fmt.Sprintf("no assets found for project (%s)", projectName))
 47+		return nil
 48+	}
 49+	c.output(fmt.Sprintf("found (%d) assets for project (%s), removing", len(fileList), projectName))
 50+
 51 	for _, file := range fileList {
 52-		intent := []byte(fmt.Sprintf("deleted (%s)\n", file.Name()))
 53+		intent := fmt.Sprintf("deleted (%s)", file.Name())
 54 		if c.write {
 55 			err = c.store.DeleteFile(bucket, file.Name())
 56 			if err == nil {
 57-				_, _ = c.session.Write(intent)
 58+				c.output(intent)
 59 			} else {
 60 				return err
 61 			}
 62 		} else {
 63-			_, _ = c.session.Write(intent)
 64+			c.output(intent)
 65 		}
 66 	}
 67 	return nil
 68 }
 69 
 70 func (c *Cmd) help() {
 71-	_, _ = c.session.Write([]byte(getHelpText(c.user.Name, "project-a")))
 72+	c.output(getHelpText(c.user.Name, "project-a"))
 73 }
 74 
 75 func (c *Cmd) stats(maxSize int) error {
 76@@ -96,8 +123,8 @@ func (c *Cmd) stats(maxSize int) error {
 77 		shared.BytesToGB(maxSize),
 78 		(float32(totalFileSize)/float32(maxSize))*100,
 79 	)
 80-	str += fmt.Sprintf("projects:\t%d\n", len(projects))
 81-	_, _ = c.session.Write([]byte(str))
 82+	str += fmt.Sprintf("projects:\t%d", len(projects))
 83+	c.output(str)
 84 
 85 	return nil
 86 }
 87@@ -109,16 +136,15 @@ func (c *Cmd) ls() error {
 88 	}
 89 
 90 	if len(projects) == 0 {
 91-		out := "no linked projects found\n"
 92-		_, _ = c.session.Write([]byte(out))
 93+		c.output("no projects found")
 94 	}
 95 
 96 	for _, project := range projects {
 97-		out := fmt.Sprintf("%s (links to: %s)\n", project.Name, project.ProjectDir)
 98+		out := fmt.Sprintf("%s (links to: %s)", project.Name, project.ProjectDir)
 99 		if project.Name == project.ProjectDir {
100-			out = fmt.Sprintf("%s\n", project.Name)
101+			out = project.Name
102 		}
103-		_, _ = c.session.Write([]byte(out))
104+		c.output(out)
105 	}
106 
107 	return nil
108@@ -135,6 +161,7 @@ func (c *Cmd) unlink(projectName string) error {
109 	if err != nil {
110 		return err
111 	}
112+	c.output(fmt.Sprintf("(%s) unlinked", project.Name))
113 
114 	return nil
115 }
116@@ -153,16 +180,16 @@ func (c *Cmd) link(projectName, linkTo string) error {
117 	projectID := ""
118 	if err == nil {
119 		projectID = project.ID
120-		c.log.Infof("user (%s) already has project (%s), updating ...", c.user.Name, projectName)
121+		c.log.Infof("user (%s) already has project (%s), updating", c.user.Name, projectName)
122 		err = c.dbpool.LinkToProject(c.user.ID, project.ID, projectDir, c.write)
123 		if err != nil {
124 			return err
125 		}
126 	} else {
127-		c.log.Infof("user (%s) has no project record (%s), creating ...", c.user.Name, projectName)
128+		c.log.Infof("user (%s) has no project record (%s), creating", c.user.Name, projectName)
129 		if !c.write {
130-			out := fmt.Sprintf("(%s) cannot create a new project without `--write` permission, aborting ...\n", projectName)
131-			_, _ = c.session.Write([]byte(out))
132+			out := fmt.Sprintf("(%s) cannot create a new project without `--write` permission, aborting", projectName)
133+			c.output(out)
134 			return nil
135 		}
136 		id, err := c.dbpool.InsertProject(c.user.ID, projectName, projectName)
137@@ -172,22 +199,22 @@ func (c *Cmd) link(projectName, linkTo string) error {
138 		projectID = id
139 	}
140 
141-	c.log.Infof("user (%s) linking (%s) to (%s) ...", c.user.Name, projectName, projectDir)
142+	c.log.Infof("user (%s) linking (%s) to (%s)", c.user.Name, projectName, projectDir)
143 	err = c.dbpool.LinkToProject(c.user.ID, projectID, projectDir, c.write)
144 	if err != nil {
145 		return err
146 	}
147 
148-	out := fmt.Sprintf("(%s) might have orphaned assets, removing ...\n", projectName)
149-	_, _ = c.session.Write([]byte(out))
150+	out := fmt.Sprintf("(%s) might have orphaned assets, removing", projectName)
151+	c.output(out)
152 
153 	err = c.rmProjectAssets(projectName)
154 	if err != nil {
155 		return err
156 	}
157 
158-	out = fmt.Sprintf("(%s) now points to (%s)\n", projectName, linkTo)
159-	_, _ = c.session.Write([]byte(out))
160+	out = fmt.Sprintf("(%s) now points to (%s)", projectName, linkTo)
161+	c.output(out)
162 	return nil
163 }
164 
165@@ -198,17 +225,17 @@ func (c *Cmd) depends(projectName string) error {
166 	}
167 
168 	if len(projects) == 0 {
169-		out := fmt.Sprintf("no projects linked to this project (%s) found\n", projectName)
170-		_, _ = c.session.Write([]byte(out))
171+		out := fmt.Sprintf("no projects linked to this project (%s) found", projectName)
172+		c.output(out)
173 		return nil
174 	}
175 
176 	for _, project := range projects {
177-		out := fmt.Sprintf("%s (links to: %s)\n", project.Name, project.ProjectDir)
178+		out := fmt.Sprintf("%s (links to: %s)", project.Name, project.ProjectDir)
179 		if project.Name == project.ProjectDir {
180-			out = fmt.Sprintf("%s\n", project.Name)
181+			out = project.Name
182 		}
183-		_, _ = c.session.Write([]byte(out))
184+		c.output(out)
185 	}
186 
187 	return nil
188@@ -218,6 +245,8 @@ func (c *Cmd) depends(projectName string) error {
189 // but keep the latest N records.
190 func (c *Cmd) prune(prefix string, keepNumLatest int) error {
191 	c.log.Infof("user (%s) running `clean` command for (%s)", c.user.Name, prefix)
192+	c.output(fmt.Sprintf("searching for projects that match prefix (%s) and are not linked to other projects", prefix))
193+
194 	if prefix == "" || prefix == "*" {
195 		e := fmt.Errorf("must provide valid prefix")
196 		return e
197@@ -228,6 +257,11 @@ func (c *Cmd) prune(prefix string, keepNumLatest int) error {
198 		return err
199 	}
200 
201+	if len(projects) == 0 {
202+		c.output(fmt.Sprintf("no projects found matching prefix (%s)", prefix))
203+		return nil
204+	}
205+
206 	rmProjects := []*db.Project{}
207 	for _, project := range projects {
208 		links, err := c.dbpool.FindProjectLinks(c.user.ID, project.Name)
209@@ -236,9 +270,10 @@ func (c *Cmd) prune(prefix string, keepNumLatest int) error {
210 		}
211 
212 		if len(links) == 0 {
213-			out := fmt.Sprintf("project (%s) is available to delete\n", project.Name)
214-			_, _ = c.session.Write([]byte(out))
215 			rmProjects = append(rmProjects, project)
216+		} else {
217+			out := fmt.Sprintf("project (%s) has (%d) projects linked to it, cannot prune", project.Name, len(projects))
218+			c.output(out)
219 		}
220 	}
221 
222@@ -248,16 +283,18 @@ func (c *Cmd) prune(prefix string, keepNumLatest int) error {
223 	}
224 
225 	for _, project := range goodbye {
226+		out := fmt.Sprintf("project (%s) is available to be pruned", project.Name)
227+		c.output(out)
228 		err = c.rmProjectAssets(project.Name)
229 		if err != nil {
230 			return err
231 		}
232 
233-		out := fmt.Sprintf("(%s) removing ...\n", project.Name)
234-		_, _ = c.session.Write([]byte(out))
235+		out = fmt.Sprintf("(%s) removing", project.Name)
236+		c.output(out)
237 
238 		if c.write {
239-			c.log.Infof("(%s) removing ...", project.Name)
240+			c.log.Infof("(%s) removing", project.Name)
241 			err = c.dbpool.RemoveProject(project.ID)
242 			if err != nil {
243 				return err
244@@ -272,7 +309,7 @@ func (c *Cmd) rm(projectName string) error {
245 	c.log.Infof("user (%s) running `rm` command for (%s)", c.user.Name, projectName)
246 	project, err := c.dbpool.FindProjectByName(c.user.ID, projectName)
247 	if err == nil {
248-		c.log.Infof("found project (%s) (%s), checking dependencies ...", projectName, project.ID)
249+		c.log.Infof("found project (%s) (%s), checking dependencies", projectName, project.ID)
250 
251 		links, err := c.dbpool.FindProjectLinks(c.user.ID, projectName)
252 		if err != nil {
253@@ -280,14 +317,14 @@ func (c *Cmd) rm(projectName string) error {
254 		}
255 
256 		if len(links) > 0 {
257-			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))
258+			e := fmt.Errorf("project (%s) has (%d) projects linking to it, cannot delete project until they have been unlinked or removed, aborting", projectName, len(links))
259 			return e
260 		}
261 
262-		out := fmt.Sprintf("(%s) removing ...\n", project.Name)
263-		_, _ = c.session.Write([]byte(out))
264+		out := fmt.Sprintf("(%s) removing", project.Name)
265+		c.output(out)
266 		if c.write {
267-			c.log.Infof("(%s) removing ...", project.Name)
268+			c.log.Infof("(%s) removing", project.Name)
269 			err = c.dbpool.RemoveProject(project.ID)
270 			if err != nil {
271 				return err
272@@ -301,18 +338,3 @@ func (c *Cmd) rm(projectName string) error {
273 	err = c.rmProjectAssets(project.Name)
274 	return err
275 }
276-
277-func (c *Cmd) bail(err error) {
278-	if err == nil {
279-		return
280-	}
281-	c.log.Error(err)
282-	utils.ErrorHandler(c.session, err)
283-}
284-
285-func (c *Cmd) notice() {
286-	if !c.write {
287-		out := "\nNOTICE: changes not commited, use `--write` to save operation\n"
288-		_, _ = c.session.Write([]byte(out))
289-	}
290-}