package dbrepo import ( "context" "errors" "go-udemy-web-1/internal/models" "time" "golang.org/x/crypto/bcrypt" ) func (m *postgresDBRepo) AllUsers() bool { return true } // InsertReservation inserts a reservation into the database func (m *postgresDBRepo) InsertReservation(res models.Reservation) (int, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() var newId int // statement stmt := `insert into reservations (first_name, last_name, email, phone, start_date, end_date, room_id, created_at, updated_at) values ($1, $2, $3, $4, $5, $6, $7, $8, $9) returning id` row := m.DB.QueryRowContext(ctx, stmt, res.FirstName, res.LastName, res.Email, res.Phone, res.StartDate, res.EndDate, res.RoomID, time.Now(), time.Now()) err := row.Scan(&newId) if err != nil { return 0, err } return newId, nil } // InsertRoomRestriction inserts a room restriction into the database func (m *postgresDBRepo) InsertRoomRestriction(r models.RoomRestriction) error { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() stmt := `insert into room_restrictions ( start_date, end_date, room_id, reservation_id, restriction_id, created_at, updated_at) values ($1, $2, $3, $4, $5, $6, $7)` _, err := m.DB.ExecContext(ctx, stmt, r.StartDate, r.EndDate, r.RoomID, r.ReservationID, r.RestrictionID, time.Now(), time.Now()) if err != nil { return err } return nil } // SearchAvailabilityByDatesByRoomID returns true if availability exists for roomID, and false if no availability func (m *postgresDBRepo) SearchAvailabilityByDatesByRoomID(start, end time.Time, roomID int) (bool, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() var numRows int stmt := `select count(id) from room_restrictions where room_id = $1 and $2 < end_date and $3> start_date` row := m.DB.QueryRowContext(ctx, stmt, roomID, start, end) err := row.Scan(&numRows) if err != nil { return false, err } if numRows == 0 { return true, nil } return false, nil } // SearchAvailabilityForAllRooms returns a slice of rooms, if any, for given date range func (m *postgresDBRepo) SearchAvailabilityForAllRooms(start, end time.Time) ([]models.Room, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() var rooms []models.Room stmt := `select r.id, r.room_name from rooms r where r.id not in (select room_id from room_restrictions rr where $1 < rr.end_date and $2> rr.start_date)` rows, err := m.DB.QueryContext(ctx, stmt, start, end) if err != nil { return nil, err } defer rows.Close() for rows.Next() { var room models.Room err := rows.Scan(&room.ID, &room.RoomName) if err != nil { return rooms, err } rooms = append(rooms, room) } if err = rows.Err(); err != nil { return rooms, err } return rooms, nil } // GetRoomById gets a room by id func (m *postgresDBRepo) GetRoomById(id int) (models.Room, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() var room models.Room stmt := `select id, room_name, created_at, updated_at from rooms where id = $1` row := m.DB.QueryRowContext(ctx, stmt, id) err := row.Scan(&room.ID, &room.RoomName, &room.CreatedAt, &room.UpdatedAt) if err != nil { return room, err } return room, nil } // GetUserByID gets a user by id func (m *postgresDBRepo) GetUserByID(id int) (models.User, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() query := `select id, first_name, last_name, email, password, access_level, created_at, updated_at from users where id = $1` row := m.DB.QueryRowContext(ctx, query, id) var u models.User err := row.Scan( &u.ID, &u.FirstName, &u.LastName, &u.Email, &u.Password, &u.AccessLevel, &u.CreatedAt, &u.UpdatedAt, ) if err != nil { return models.User{}, err } return u, nil } // UpdateUser updates a user in the database func (m *postgresDBRepo) UpdateUser(u models.User) error { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() query := `update users set first_name = $1, last_name = $2, email = $3, access_level = $4, updated_at = $5` _, err := m.DB.ExecContext(ctx, query, u.FirstName, u.LastName, u.Email, u.AccessLevel, time.Now()) if err != nil { return err } return nil } // Authenticate authenticates a user func (m *postgresDBRepo) Authenticate(email, testPassword string) (int, string, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() var id int var hashedPassword string row := m.DB.QueryRowContext(ctx, "select id, password from users where email = $1", email) err := row.Scan(&id, &hashedPassword) if err != nil { return id, "", err } err = bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(testPassword)) if err == bcrypt.ErrMismatchedHashAndPassword { return 0, "", errors.New("incorrect password") } else if err != nil { return 0, "", err } return id, hashedPassword, nil } // AllReservations returns a slice of all reservations func (m *postgresDBRepo) AllReservations() ([]models.Reservation, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() var reservations []models.Reservation query := `select r.id, r.first_name, r.last_name, r.email, r.phone, r.start_date, r.end_date, r.room_id, r.created_at, r.updated_at, r.processed, rm.id, rm.room_name from reservations r left join rooms rm on (r.room_id = rm.id) order by r.start_date asc` rows, err := m.DB.QueryContext(ctx, query) if err != nil { return reservations, err } defer rows.Close() // To avoid memory leak for rows.Next() { var i models.Reservation err := rows.Scan( &i.ID, &i.FirstName, &i.LastName, &i.Email, &i.Phone, &i.StartDate, &i.EndDate, &i.RoomID, &i.CreatedAt, &i.UpdatedAt, &i.Processed, &i.Room.ID, &i.Room.RoomName, ) if err != nil { return reservations, err } reservations = append(reservations, i) } return reservations, nil } // AllNewReservations returns a slice of all new reservations func (m *postgresDBRepo) AllNewReservations() ([]models.Reservation, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() var reservations []models.Reservation query := `select r.id, r.first_name, r.last_name, r.email, r.phone, r.start_date, r.end_date, r.room_id, r.created_at, r.updated_at, r.processed, rm.id, rm.room_name from reservations r left join rooms rm on (r.room_id = rm.id) where r.processed = 0 order by r.start_date asc` rows, err := m.DB.QueryContext(ctx, query) if err != nil { return reservations, err } defer rows.Close() // To avoid memory leak for rows.Next() { var i models.Reservation err := rows.Scan( &i.ID, &i.FirstName, &i.LastName, &i.Email, &i.Phone, &i.StartDate, &i.EndDate, &i.RoomID, &i.CreatedAt, &i.UpdatedAt, &i.Processed, &i.Room.ID, &i.Room.RoomName, ) if err != nil { return reservations, err } reservations = append(reservations, i) } return reservations, nil } // GetReservationByID returns one reservation by ID func (m *postgresDBRepo) GetReservationByID(id int) (models.Reservation, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() var res models.Reservation query := `select r.id, r.first_name, r.last_name, r.email, r.phone, r.start_date, r.end_date, r.room_id, r.created_at, r.updated_at, r.processed, rm.id, rm.room_name from reservations r left join rooms rm on (r.room_id = rm.id) where r.id = $1` row := m.DB.QueryRowContext(ctx, query, id) err := row.Scan( &res.ID, &res.FirstName, &res.LastName, &res.Email, &res.Phone, &res.StartDate, &res.EndDate, &res.RoomID, &res.CreatedAt, &res.UpdatedAt, &res.Processed, &res.Room.ID, &res.Room.RoomName, ) if err != nil { return res, err } return res, nil }