- OpenAPI: add missing endpoints (add-from-url, subscriptions, public availability) - OpenAPI: CalendarSubscription schema, Subscriptions tag - Frontend app - Migrations: count_for_availability, subscriptions_sync, user_preferences, calendar_settings - Config, rate limit, auth, calendar, booking, ICS, availability, user service updates Made-with: Cursor
118 lines
3.4 KiB
Go
118 lines
3.4 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/calendarapi/internal/models"
|
|
"github.com/calendarapi/internal/repository"
|
|
"github.com/calendarapi/internal/utils"
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
type UserService struct {
|
|
pool *pgxpool.Pool
|
|
queries *repository.Queries
|
|
audit *AuditService
|
|
}
|
|
|
|
func NewUserService(pool *pgxpool.Pool, queries *repository.Queries, audit *AuditService) *UserService {
|
|
return &UserService{pool: pool, queries: queries, audit: audit}
|
|
}
|
|
|
|
func (s *UserService) GetMe(ctx context.Context, userID uuid.UUID) (*models.User, error) {
|
|
u, err := s.queries.GetUserByID(ctx, utils.ToPgUUID(userID))
|
|
if err != nil {
|
|
if err == pgx.ErrNoRows {
|
|
return nil, models.ErrNotFound
|
|
}
|
|
return nil, models.ErrInternal
|
|
}
|
|
user := userFromIDRow(u)
|
|
return &user, nil
|
|
}
|
|
|
|
type UserUpdateInput struct {
|
|
Timezone *string
|
|
WeekStartDay *int
|
|
DateFormat *string
|
|
TimeFormat *string
|
|
DefaultEventDurationMinutes *int
|
|
DefaultReminderMinutes *int
|
|
ShowWeekends *bool
|
|
WorkingHoursStart *string
|
|
WorkingHoursEnd *string
|
|
NotificationsEmail *bool
|
|
}
|
|
|
|
func (s *UserService) Update(ctx context.Context, userID uuid.UUID, in *UserUpdateInput) (*models.User, error) {
|
|
if in == nil {
|
|
in = &UserUpdateInput{}
|
|
}
|
|
if in.Timezone != nil {
|
|
if err := utils.ValidateTimezone(*in.Timezone); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
u, err := s.queries.UpdateUser(ctx, repository.UpdateUserParams{
|
|
ID: utils.ToPgUUID(userID),
|
|
Timezone: utils.ToPgTextPtr(in.Timezone),
|
|
WeekStartDay: utils.ToPgInt2Ptr(in.WeekStartDay),
|
|
DateFormat: utils.ToPgTextPtr(in.DateFormat),
|
|
TimeFormat: utils.ToPgTextPtr(in.TimeFormat),
|
|
DefaultEventDurationMinutes: utils.ToPgInt4Ptr(in.DefaultEventDurationMinutes),
|
|
DefaultReminderMinutes: utils.ToPgInt4Ptr(in.DefaultReminderMinutes),
|
|
ShowWeekends: utils.ToPgBoolPtr(in.ShowWeekends),
|
|
WorkingHoursStart: utils.ToPgTextPtr(in.WorkingHoursStart),
|
|
WorkingHoursEnd: utils.ToPgTextPtr(in.WorkingHoursEnd),
|
|
NotificationsEmail: utils.ToPgBoolPtr(in.NotificationsEmail),
|
|
})
|
|
if err != nil {
|
|
if err == pgx.ErrNoRows {
|
|
return nil, models.ErrNotFound
|
|
}
|
|
return nil, models.ErrInternal
|
|
}
|
|
user := userFromUpdateRow(u)
|
|
return &user, nil
|
|
}
|
|
|
|
func (s *UserService) Delete(ctx context.Context, userID uuid.UUID) error {
|
|
tx, err := s.pool.Begin(ctx)
|
|
if err != nil {
|
|
return models.ErrInternal
|
|
}
|
|
defer tx.Rollback(ctx)
|
|
|
|
qtx := s.queries.WithTx(tx)
|
|
pgID := utils.ToPgUUID(userID)
|
|
|
|
if err := qtx.SoftDeleteContactsByOwner(ctx, pgID); err != nil {
|
|
return models.ErrInternal
|
|
}
|
|
if err := qtx.SoftDeleteEventsByCreator(ctx, pgID); err != nil {
|
|
return models.ErrInternal
|
|
}
|
|
if err := qtx.SoftDeleteCalendarsByOwner(ctx, pgID); err != nil {
|
|
return models.ErrInternal
|
|
}
|
|
if err := qtx.RevokeAllUserAPIKeys(ctx, pgID); err != nil {
|
|
return models.ErrInternal
|
|
}
|
|
if err := qtx.RevokeAllUserRefreshTokens(ctx, pgID); err != nil {
|
|
return models.ErrInternal
|
|
}
|
|
if err := qtx.SoftDeleteUser(ctx, pgID); err != nil {
|
|
return models.ErrInternal
|
|
}
|
|
|
|
if err := tx.Commit(ctx); err != nil {
|
|
return models.ErrInternal
|
|
}
|
|
|
|
s.audit.Log(ctx, "user", userID, "DELETE_USER", userID)
|
|
return nil
|
|
}
|