Files
encore-test/profiles/personal_data.go
fabio 1367829f80 feat: implement password reset functionality
- Added PasswordResetPage component for user password reset.
- Integrated password reset link in LoginPage with a router link.
- Created API endpoints for requesting and confirming password resets.
- Added email templates for password reset notifications.
- Updated database schema to support password reset tokens.
- Enhanced ProfilePage and EditProfileDialog to include personal data fields.
- Introduced layout store to manage body dimensions for responsive design.
- Added validation for personal data fields in profile management.
2026-07-26 17:28:51 +02:00

117 lines
3.6 KiB
Go

package profiles
import (
"context"
"errors"
"time"
"encore.dev/beta/auth"
"encore.dev/beta/errs"
"encore.dev/storage/sqldb"
"encore.dev/types/uuid"
"github.com/jackc/pgx/v5/pgconn"
)
// PersonalData holds the personal details linked one-to-one to a user profile.
type PersonalData struct {
UserID uuid.UUID `json:"user_id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Address string `json:"address"`
Cap string `json:"cap"`
City string `json:"city"`
Country string `json:"country"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// PersonalDataParams are the editable fields of a user's personal data.
type PersonalDataParams struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Address string `json:"address"`
Cap string `json:"cap"`
City string `json:"city"`
Country string `json:"country"`
}
// GetPersonalData returns the authenticated user's own personal data.
//
//encore:api auth method=GET path=/profiles/personal-data
func GetPersonalData(ctx context.Context) (*PersonalData, error) {
userID, err := authedUserID()
if err != nil {
return nil, err
}
pd := PersonalData{UserID: userID}
err = db.QueryRow(ctx, `
SELECT first_name, last_name, address, cap, city, country, created_at, updated_at
FROM personal_data WHERE user_id = $1
`, userID).Scan(&pd.FirstName, &pd.LastName, &pd.Address, &pd.Cap, &pd.City, &pd.Country, &pd.CreatedAt, &pd.UpdatedAt)
if errors.Is(err, sqldb.ErrNoRows) {
return nil, &errs.Error{Code: errs.NotFound, Message: "personal data not found"}
}
if err != nil {
return nil, errs.WrapCode(err, errs.Internal, "failed to fetch personal data")
}
return &pd, nil
}
// UpsertPersonalData creates or replaces the authenticated user's own personal data.
//
//encore:api auth method=PUT path=/profiles/personal-data
func UpsertPersonalData(ctx context.Context, p *PersonalDataParams) (*PersonalData, error) {
userID, err := authedUserID()
if err != nil {
return nil, err
}
pd := PersonalData{
UserID: userID,
FirstName: p.FirstName,
LastName: p.LastName,
Address: p.Address,
Cap: p.Cap,
City: p.City,
Country: p.Country,
}
err = db.QueryRow(ctx, `
INSERT INTO personal_data (user_id, first_name, last_name, address, cap, city, country)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (user_id) DO UPDATE
SET first_name = EXCLUDED.first_name,
last_name = EXCLUDED.last_name,
address = EXCLUDED.address,
cap = EXCLUDED.cap,
city = EXCLUDED.city,
country = EXCLUDED.country,
updated_at = NOW()
RETURNING created_at, updated_at
`, userID, p.FirstName, p.LastName, p.Address, p.Cap, p.City, p.Country).Scan(&pd.CreatedAt, &pd.UpdatedAt)
if isForeignKeyViolation(err) {
return nil, &errs.Error{Code: errs.NotFound, Message: "profile not found"}
}
if err != nil {
return nil, errs.WrapCode(err, errs.Internal, "failed to save personal data")
}
return &pd, nil
}
// authedUserID returns the UUID of the authenticated caller.
func authedUserID() (uuid.UUID, error) {
uid, ok := auth.UserID()
if !ok {
return uuid.Nil, &errs.Error{Code: errs.Unauthenticated, Message: "authentication required"}
}
userID, err := uuid.FromString(string(uid))
if err != nil {
return uuid.Nil, errs.WrapCode(err, errs.Internal, "invalid user id")
}
return userID, nil
}
// isForeignKeyViolation reports whether err is a Postgres foreign key constraint violation.
func isForeignKeyViolation(err error) bool {
var pgErr *pgconn.PgError
return errors.As(err, &pgErr) && pgErr.Code == "23503"
}