feat(backend): add data export
This commit is contained in:
parent
ded9d06e4a
commit
15109819df
13 changed files with 559 additions and 4 deletions
|
@ -221,10 +221,26 @@ func (db *DB) DeleteMemberAvatar(ctx context.Context, memberID xid.ID, hash stri
|
|||
return errors.Wrap(err, "deleting webp avatar")
|
||||
}
|
||||
|
||||
err = db.minio.RemoveObject(ctx, db.minioBucket, "/members/"+memberID.String()+"/"+hash+".webp", minio.RemoveObjectOptions{})
|
||||
err = db.minio.RemoveObject(ctx, db.minioBucket, "/members/"+memberID.String()+"/"+hash+".jpg", minio.RemoveObjectOptions{})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "deleting jpeg avatar")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) UserAvatar(ctx context.Context, userID xid.ID, hash string) (io.ReadCloser, error) {
|
||||
obj, err := db.minio.GetObject(ctx, db.minioBucket, "/users/"+userID.String()+"/"+hash+".webp", minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getting object")
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func (db *DB) MemberAvatar(ctx context.Context, memberID xid.ID, hash string) (io.ReadCloser, error) {
|
||||
obj, err := db.minio.GetObject(ctx, db.minioBucket, "/members/"+memberID.String()+"/"+hash+".webp", minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getting object")
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
|
104
backend/db/export.go
Normal file
104
backend/db/export.go
Normal file
|
@ -0,0 +1,104 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"emperror.dev/errors"
|
||||
"github.com/georgysavva/scany/pgxscan"
|
||||
"github.com/jackc/pgx/v4"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/rs/xid"
|
||||
)
|
||||
|
||||
type DataExport struct {
|
||||
ID int64
|
||||
UserID xid.ID
|
||||
Filename string
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func (de DataExport) Path() string {
|
||||
return "/exports/" + de.UserID.String() + "/" + de.Filename + ".zip"
|
||||
}
|
||||
|
||||
const ErrNoExport = errors.Sentinel("no data export exists")
|
||||
|
||||
const KeepExportTime = 7 * 24 * time.Hour
|
||||
|
||||
func (db *DB) UserExport(ctx context.Context, userID xid.ID) (de DataExport, err error) {
|
||||
sql, args, err := sq.Select("*").
|
||||
From("data_exports").
|
||||
Where("user_id = ?", userID).
|
||||
OrderBy("id DESC").
|
||||
Limit(1).ToSql()
|
||||
if err != nil {
|
||||
return de, errors.Wrap(err, "building query")
|
||||
}
|
||||
|
||||
err = pgxscan.Get(ctx, db, &de, sql, args...)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == pgx.ErrNoRows {
|
||||
return de, ErrNoExport
|
||||
}
|
||||
|
||||
return de, errors.Wrap(err, "executing sql")
|
||||
}
|
||||
return de, nil
|
||||
}
|
||||
|
||||
const recentExport = 24 * time.Hour
|
||||
|
||||
func (db *DB) HasRecentExport(ctx context.Context, userID xid.ID) (hasExport bool, err error) {
|
||||
err = db.QueryRow(ctx,
|
||||
"SELECT EXISTS(SELECT * FROM data_exports WHERE user_id = $1 AND created_at > $2)",
|
||||
userID, time.Now().Add(-recentExport)).Scan(&hasExport)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "executing query")
|
||||
}
|
||||
return hasExport, nil
|
||||
}
|
||||
|
||||
func (db *DB) CreateExport(ctx context.Context, userID xid.ID, filename string, file *bytes.Buffer) (de DataExport, err error) {
|
||||
de = DataExport{
|
||||
UserID: userID,
|
||||
Filename: filename,
|
||||
}
|
||||
|
||||
_, err = db.minio.PutObject(ctx, db.minioBucket, de.Path(), file, int64(file.Len()), minio.PutObjectOptions{
|
||||
ContentType: "application/zip",
|
||||
})
|
||||
if err != nil {
|
||||
return de, errors.Wrap(err, "writing export file")
|
||||
}
|
||||
|
||||
sql, args, err := sq.Insert("data_exports").Columns("user_id", "filename").Values(userID, filename).ToSql()
|
||||
if err != nil {
|
||||
return de, errors.Wrap(err, "building query")
|
||||
}
|
||||
|
||||
pgxscan.Get(ctx, db, &de, sql, args...)
|
||||
if err != nil {
|
||||
return de, errors.Wrap(err, "executing sql")
|
||||
}
|
||||
return de, nil
|
||||
}
|
||||
|
||||
func (db *DB) DeleteExport(ctx context.Context, de DataExport) (err error) {
|
||||
sql, args, err := sq.Delete("data_exports").Where("id = ?", de.ID).ToSql()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "building query")
|
||||
}
|
||||
|
||||
err = db.minio.RemoveObject(ctx, db.minioBucket, de.Path(), minio.RemoveObjectOptions{})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "deleting export zip")
|
||||
}
|
||||
|
||||
_, err = db.Exec(ctx, sql, args...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "executing sql")
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -61,7 +61,7 @@ func (db *DB) UserMember(ctx context.Context, userID xid.ID, memberRef string) (
|
|||
|
||||
// UserMembers returns all of a user's members, sorted by name.
|
||||
func (db *DB) UserMembers(ctx context.Context, userID xid.ID) (ms []Member, err error) {
|
||||
sql, args, err := sq.Select("id", "user_id", "name", "display_name", "bio", "avatar", "names", "pronouns").
|
||||
sql, args, err := sq.Select("*").
|
||||
From("members").Where("user_id = ?", userID).
|
||||
OrderBy("name", "id").ToSql()
|
||||
if err != nil {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue