4facc6b371
* implement avatar backend functionality * add avatar endpoints * typo * implement client side avatar upload and handling * fixes * Update go/internal/handler/handler.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update js/desktop/src/lib/avatar-image.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * bug in order * remove unused component * fix invalid migration * fix syntax errors --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
177 lines
4.7 KiB
Go
177 lines
4.7 KiB
Go
package human
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"go.jetify.com/typeid"
|
|
)
|
|
|
|
var errNotFound = errors.New("not found")
|
|
|
|
type humanIDPrefix struct{}
|
|
|
|
func (humanIDPrefix) Prefix() string { return "human" }
|
|
|
|
type humanID struct {
|
|
typeid.TypeID[humanIDPrefix]
|
|
}
|
|
|
|
func newHumanID() (humanID, error) {
|
|
return typeid.New[humanID]()
|
|
}
|
|
|
|
func emailPrefix(email string) string {
|
|
return strings.Split(email, "@")[0]
|
|
}
|
|
|
|
type repository interface {
|
|
getByEmail(ctx context.Context, email string) (*Human, error)
|
|
getByID(ctx context.Context, id string) (*Human, error)
|
|
create(ctx context.Context, email string) (*Human, error)
|
|
exists(ctx context.Context, email string) (bool, error)
|
|
listAll(ctx context.Context) ([]*Human, error)
|
|
updateEmailNotificationsEnabled(ctx context.Context, id string, enabled bool) error
|
|
updateLastEmailNotificationSentAt(ctx context.Context, id string, t time.Time) error
|
|
updateAvatarObjectID(ctx context.Context, id string, objectID *string) error
|
|
}
|
|
|
|
type repositoryImpl struct {
|
|
pool *pgxpool.Pool
|
|
}
|
|
|
|
func newRepository(pool *pgxpool.Pool) repository {
|
|
return &repositoryImpl{pool: pool}
|
|
}
|
|
|
|
func (r *repositoryImpl) getByEmail(ctx context.Context, email string) (*Human, error) {
|
|
var h Human
|
|
err := r.pool.QueryRow(ctx,
|
|
`SELECT id, email, email_notifications_enabled, last_email_notification_sent_at, created_at, avatar_object_id FROM humans WHERE email = $1`,
|
|
email,
|
|
).Scan(&h.ID, &h.Email, &h.EmailNotificationsEnabled, &h.LastEmailNotificationSentAt, &h.CreatedAt, &h.AvatarObjectID)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, errNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
h.EmailPrefix = emailPrefix(h.Email)
|
|
return &h, nil
|
|
}
|
|
|
|
func (r *repositoryImpl) getByID(ctx context.Context, id string) (*Human, error) {
|
|
var h Human
|
|
err := r.pool.QueryRow(ctx,
|
|
`SELECT id, email, email_notifications_enabled, last_email_notification_sent_at, created_at, avatar_object_id FROM humans WHERE id = $1`,
|
|
id,
|
|
).Scan(&h.ID, &h.Email, &h.EmailNotificationsEnabled, &h.LastEmailNotificationSentAt, &h.CreatedAt, &h.AvatarObjectID)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, errNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
h.EmailPrefix = emailPrefix(h.Email)
|
|
return &h, nil
|
|
}
|
|
|
|
func (r *repositoryImpl) create(ctx context.Context, email string) (*Human, error) {
|
|
id, err := newHumanID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var h Human
|
|
err = r.pool.QueryRow(ctx,
|
|
`INSERT INTO humans (id, email) VALUES ($1, $2)
|
|
RETURNING id, email, email_notifications_enabled, last_email_notification_sent_at, created_at`,
|
|
id.String(), email,
|
|
).Scan(&h.ID, &h.Email, &h.EmailNotificationsEnabled, &h.LastEmailNotificationSentAt, &h.CreatedAt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
h.EmailPrefix = emailPrefix(h.Email)
|
|
return &h, nil
|
|
}
|
|
|
|
func (r *repositoryImpl) exists(ctx context.Context, email string) (bool, error) {
|
|
var exists bool
|
|
err := r.pool.QueryRow(ctx,
|
|
`SELECT EXISTS(SELECT 1 FROM humans WHERE email = $1)`,
|
|
email,
|
|
).Scan(&exists)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return exists, nil
|
|
}
|
|
|
|
func (r *repositoryImpl) listAll(ctx context.Context) ([]*Human, error) {
|
|
rows, err := r.pool.Query(ctx,
|
|
`SELECT id, email, email_notifications_enabled, last_email_notification_sent_at, created_at, avatar_object_id FROM humans`,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var humans []*Human
|
|
for rows.Next() {
|
|
var h Human
|
|
if err := rows.Scan(&h.ID, &h.Email, &h.EmailNotificationsEnabled, &h.LastEmailNotificationSentAt, &h.CreatedAt, &h.AvatarObjectID); err != nil {
|
|
return nil, err
|
|
}
|
|
h.EmailPrefix = emailPrefix(h.Email)
|
|
humans = append(humans, &h)
|
|
}
|
|
return humans, rows.Err()
|
|
}
|
|
|
|
func (r *repositoryImpl) updateEmailNotificationsEnabled(ctx context.Context, id string, enabled bool) error {
|
|
result, err := r.pool.Exec(ctx,
|
|
`UPDATE humans SET email_notifications_enabled = $2 WHERE id = $1`,
|
|
id, enabled,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if result.RowsAffected() == 0 {
|
|
return errNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *repositoryImpl) updateLastEmailNotificationSentAt(ctx context.Context, id string, t time.Time) error {
|
|
result, err := r.pool.Exec(ctx,
|
|
`UPDATE humans SET last_email_notification_sent_at = $2 WHERE id = $1`,
|
|
id, t,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if result.RowsAffected() == 0 {
|
|
return errNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *repositoryImpl) updateAvatarObjectID(ctx context.Context, id string, objectID *string) error {
|
|
result, err := r.pool.Exec(ctx,
|
|
`UPDATE humans SET avatar_object_id = $2 WHERE id = $1`,
|
|
id, objectID,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if result.RowsAffected() == 0 {
|
|
return errNotFound
|
|
}
|
|
return nil
|
|
}
|