Skip to content

Commit

Permalink
refactor(prose): remove concept of posts for images
Browse files Browse the repository at this point in the history
  • Loading branch information
neurosnap committed Jan 19, 2025
1 parent 4105299 commit 310e14b
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 284 deletions.
5 changes: 4 additions & 1 deletion cmd/scripts/prose-imgs-migrate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func images(logger *slog.Logger, dbh db.DB, st storage.StorageServe, bucket sst.
rdr, _, err := st.GetObject(imgBucket, posts.Filename)
if err != nil {
logger.Error("get object", "err", err)
return err
continue
}
err = upload(logger, st, bucket, posts.Filename, rdr)
if err != nil {
Expand All @@ -86,6 +86,9 @@ func main() {
bail(err)

for _, user := range users {
if user.Name != "erock" {
continue
}
logger.Info("migrating user images", "user", user.Name)

bucket, err := st.UpsertBucket(shared.GetAssetBucketName(user.ID))
Expand Down
1 change: 1 addition & 0 deletions db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ type DB interface {
InsertFeedItems(postID string, items []*FeedItem) error
FindFeedItemsByPostID(postID string) ([]*FeedItem, error)

UpsertProject(userID, name, projectDir string) (*Project, error)
InsertProject(userID, name, projectDir string) (string, error)
UpdateProject(userID, name string) error
UpdateProjectAcl(userID, name string, acl ProjectAcl) error
Expand Down
20 changes: 20 additions & 0 deletions db/postgres/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -1990,3 +1990,23 @@ func (me *PsqlDB) AddPicoPlusUser(username, email, paymentType, txId string) err

return tx.Commit()
}

func (me *PsqlDB) UpsertProject(userID, projectName, projectDir string) (*db.Project, error) {
project, err := me.FindProjectByName(userID, projectName)
if err == nil {
// this just updates the `createdAt` timestamp, useful for book-keeping
err = me.UpdateProject(userID, projectName)
if err != nil {
me.Logger.Error("could not update project", "err", err)
return nil, err
}
return project, nil
}

_, err = me.InsertProject(userID, projectName, projectName)
if err != nil {
me.Logger.Error("could not create project", "err", err)
return nil, err
}
return me.FindProjectByName(userID, projectName)
}
4 changes: 4 additions & 0 deletions db/stub/stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ func (me *StubDB) FindFeedItemsByPostID(postID string) ([]*db.FeedItem, error) {
return []*db.FeedItem{}, notImpl
}

func (me *StubDB) UpsertProject(userID, name, projectDir string) (*db.Project, error) {
return nil, notImpl
}

func (me *StubDB) InsertProject(userID, name, projectDir string) (string, error) {
return "", notImpl
}
Expand Down
225 changes: 154 additions & 71 deletions filehandlers/imgs/handler.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
package uploadimgs

import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"time"

"slices"
"strings"

"github.com/charmbracelet/ssh"
exifremove "github.com/neurosnap/go-exif-remove"
"github.com/picosh/pico/db"
"github.com/picosh/pico/shared"
"github.com/picosh/pico/shared/storage"
"github.com/picosh/pobj"
sst "github.com/picosh/pobj/storage"
sendutils "github.com/picosh/send/utils"
"github.com/picosh/utils"
)

var Space = "imgs"

type PostMetaData struct {
*db.Post
OrigText []byte
Cur *db.Post
Tags []string
User *db.User
*sendutils.FileEntry
FeatureFlag *db.FeatureFlag
Text []byte
FileSize int
TotalFileSize int
Filename string
User *db.User
FeatureFlag *db.FeatureFlag
Bucket sst.Bucket
}

type UploadImgHandler struct {
Expand All @@ -51,42 +52,81 @@ func (h *UploadImgHandler) getObjectPath(fpath string) string {
return filepath.Join("prose", fpath)
}

func (h *UploadImgHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.FileInfo, sendutils.ReaderAtCloser, error) {
func (h *UploadImgHandler) List(s ssh.Session, fpath string, isDir bool, recursive bool) ([]os.FileInfo, error) {
var fileList []os.FileInfo

user, err := h.DBPool.FindUser(s.Permissions().Extensions["user_id"])
if err != nil {
return nil, nil, err
return fileList, err
}

cleanFilename := filepath.Base(entry.Filepath)
cleanFilename := fpath

bucketName := shared.GetAssetBucketName(user.ID)
bucket, err := h.Storage.GetBucket(bucketName)
if err != nil {
return fileList, err
}

if cleanFilename == "" || cleanFilename == "." {
return nil, nil, os.ErrNotExist
name := cleanFilename
if name == "" {
name = "/"
}

info := &sendutils.VirtualFile{
FName: name,
FIsDir: true,
}

fileList = append(fileList, info)
} else {
fp := h.getObjectPath(cleanFilename)
if fp != "/" && isDir {
fp += "/"
}

foundList, err := h.Storage.ListObjects(bucket, fp, recursive)
if err != nil {
return fileList, err
}

fileList = append(fileList, foundList...)
}

post, err := h.DBPool.FindPostWithFilename(cleanFilename, user.ID, Space)
return fileList, nil
}

func (h *UploadImgHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.FileInfo, sendutils.ReaderAtCloser, error) {
user, err := h.DBPool.FindUser(s.Permissions().Extensions["user_id"])
if err != nil {
return nil, nil, err
}

fileInfo := &sendutils.VirtualFile{
FName: post.Filename,
FIsDir: false,
FSize: int64(post.FileSize),
FModTime: *post.UpdatedAt,
cleanFilename := filepath.Base(entry.Filepath)

if cleanFilename == "" || cleanFilename == "." {
return nil, nil, os.ErrNotExist
}

bucket, err := h.Storage.GetBucket(shared.GetAssetBucketName(user.ID))
if err != nil {
return nil, nil, err
}

contents, _, err := h.Storage.GetObject(bucket, h.getObjectPath(post.Filename))
contents, info, err := h.Storage.GetObject(bucket, h.getObjectPath(cleanFilename))
if err != nil {
return nil, nil, err
}

reader := pobj.NewAllReaderAt(contents)

fileInfo := &sendutils.VirtualFile{
FName: cleanFilename,
FIsDir: false,
FSize: info.Size,
FModTime: info.LastModified,
}

return fileInfo, reader, nil
}

Expand Down Expand Up @@ -125,53 +165,33 @@ func (h *UploadImgHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (str
}
}

now := time.Now()
fileSize := binary.Size(text)
shasum := utils.Shasum(text)
slug := utils.SanitizeFileExt(filename)

nextPost := db.Post{
Filename: filename,
Slug: slug,
PublishAt: &now,
Text: string(text),
MimeType: mimeType,
FileSize: fileSize,
Shasum: shasum,
}

post, err := h.DBPool.FindPostWithFilename(
nextPost.Filename,
user.ID,
Space,
)
if err != nil {
logger.Info("unable to find image, continuing", "filename", nextPost.Filename, "err", err.Error())
}

featureFlag := shared.FindPlusFF(h.DBPool, h.Cfg, user.ID)
metadata := PostMetaData{
OrigText: text,
Post: &nextPost,
User: user,
FileEntry: entry,
Cur: post,
FeatureFlag: featureFlag,
}

if post != nil {
metadata.Post.PublishAt = post.PublishAt
bucket, err := h.Storage.UpsertBucket(shared.GetAssetBucketName(user.ID))
if err != nil {
return "", err
}

err = h.writeImg(s, &metadata)
totalFileSize, err := h.Storage.GetBucketQuota(bucket)
if err != nil {
logger.Error("could not write img", "err", err.Error())
logger.Error("bucket quota", "err", err)
return "", err
}

totalFileSize, err := h.DBPool.FindTotalSizeForUser(user.ID)
metadata := PostMetaData{
Filename: filename,
FileSize: fileSize,
Text: text,
User: user,
FeatureFlag: featureFlag,
Bucket: bucket,
TotalFileSize: int(totalFileSize),
}

err = h.writeImg(&metadata)
if err != nil {
logger.Error("could not find total storage size for user", "err", err.Error())
logger.Error("could not write img", "err", err.Error())
return "", err
}

Expand All @@ -185,7 +205,7 @@ func (h *UploadImgHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (str
str := fmt.Sprintf(
"%s (space: %.2f/%.2fGB, %.2f%%)",
url,
utils.BytesToGB(totalFileSize),
utils.BytesToGB(metadata.TotalFileSize+fileSize),
utils.BytesToGB(maxSize),
(float32(totalFileSize)/float32(maxSize))*100,
)
Expand All @@ -206,30 +226,93 @@ func (h *UploadImgHandler) Delete(s ssh.Session, entry *sendutils.FileEntry) err
"filename", filename,
)

post, err := h.DBPool.FindPostWithFilename(
filename,
user.ID,
Space,
)
bucket, err := h.Storage.UpsertBucket(shared.GetAssetBucketName(user.ID))
if err != nil {
logger.Info("unable to find image, continuing", "err", err.Error())
return err
}

err = h.DBPool.RemovePosts([]string{post.ID})
logger.Info("deleting image")
err = h.Storage.DeleteObject(bucket, h.getObjectPath(filename))
if err != nil {
logger.Error("error removing image", "error", err)
return fmt.Errorf("error for %s: %v", filename, err)
return err
}

bucket, err := h.Storage.UpsertBucket(shared.GetAssetBucketName(user.ID))
return nil
}

func (h *UploadImgHandler) validateImg(data *PostMetaData) (bool, error) {
fileMax := data.FeatureFlag.Data.FileMax
if int64(data.FileSize) > fileMax {
return false, fmt.Errorf("ERROR: file (%s) has exceeded maximum file size (%d bytes)", data.Filename, fileMax)
}

storageMax := data.FeatureFlag.Data.StorageMax
if uint64(data.TotalFileSize+data.FileSize) > storageMax {
return false, fmt.Errorf("ERROR: user (%s) has exceeded (%d bytes) max (%d bytes)", data.User.Name, data.TotalFileSize, storageMax)
}

if !utils.IsExtAllowed(data.Filename, h.Cfg.AllowedExt) {
extStr := strings.Join(h.Cfg.AllowedExt, ",")
err := fmt.Errorf(
"ERROR: (%s) invalid file, format must be (%s), skipping",
data.Filename,
extStr,
)
return false, err
}

return true, nil
}

func (h *UploadImgHandler) metaImg(data *PostMetaData) error {
// if the file is empty that means we should delete it
// so we can skip all the meta info
if data.FileSize == 0 {
return nil
}

// make sure we have a bucket
bucket, err := h.Storage.UpsertBucket(shared.GetAssetBucketName(data.User.ID))
if err != nil {
return err
}

logger.Info("deleting image")
err = h.Storage.DeleteObject(bucket, h.getObjectPath(filename))
// make sure we have a prose project to upload to
_, err = h.DBPool.UpsertProject(data.User.ID, "prose", "prose")
if err != nil {
return err
}

reader := bytes.NewReader([]byte(data.Text))
_, _, err = h.Storage.PutObject(
bucket,
h.getObjectPath(data.Filename),
sendutils.NopReaderAtCloser(reader),
&sendutils.FileEntry{},
)
if err != nil {
return err
}

return nil
}

func (h *UploadImgHandler) writeImg(data *PostMetaData) error {
valid, err := h.validateImg(data)
if !valid {
return err
}

logger := h.Cfg.Logger
logger = shared.LoggerWithUser(logger, data.User)
logger = logger.With(
"filename", data.Filename,
)

logger.Info("uploading image")
err = h.metaImg(data)
if err != nil {
logger.Error("meta img", "err", err)
return err
}

Expand Down
Loading

0 comments on commit 310e14b

Please sign in to comment.