2024-07-06 20:55:25 +00:00
|
|
|
package dbrepo
|
|
|
|
|
2024-07-08 21:04:17 +00:00
|
|
|
import (
|
|
|
|
"context"
|
2024-07-19 07:44:27 +00:00
|
|
|
"errors"
|
2024-07-08 21:04:17 +00:00
|
|
|
"go-udemy-web-1/internal/models"
|
2024-07-28 12:43:39 +00:00
|
|
|
"log"
|
2024-07-08 21:04:17 +00:00
|
|
|
"time"
|
2024-07-19 07:44:27 +00:00
|
|
|
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
2024-07-08 21:04:17 +00:00
|
|
|
)
|
|
|
|
|
2024-07-06 20:55:25 +00:00
|
|
|
func (m *postgresDBRepo) AllUsers() bool {
|
|
|
|
return true
|
|
|
|
}
|
2024-07-08 21:04:17 +00:00
|
|
|
|
|
|
|
// InsertReservation inserts a reservation into the database
|
2024-07-09 20:57:07 +00:00
|
|
|
func (m *postgresDBRepo) InsertReservation(res models.Reservation) (int, error) {
|
2024-07-08 21:04:17 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
|
|
defer cancel()
|
2024-07-09 20:57:07 +00:00
|
|
|
|
|
|
|
var newId int
|
2024-07-08 21:04:17 +00:00
|
|
|
// statement
|
|
|
|
stmt := `insert into reservations (first_name, last_name, email, phone,
|
|
|
|
start_date, end_date, room_id, created_at, updated_at)
|
2024-07-09 20:57:07 +00:00
|
|
|
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)`
|
2024-07-08 21:04:17 +00:00
|
|
|
|
2024-07-09 20:57:07 +00:00
|
|
|
_, err := m.DB.ExecContext(ctx, stmt,
|
|
|
|
r.StartDate,
|
|
|
|
r.EndDate,
|
|
|
|
r.RoomID,
|
|
|
|
r.ReservationID,
|
|
|
|
r.RestrictionID,
|
|
|
|
time.Now(),
|
|
|
|
time.Now())
|
2024-07-08 21:04:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2024-07-09 21:08:31 +00:00
|
|
|
|
2024-07-09 21:50:54 +00:00
|
|
|
// 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) {
|
2024-07-09 21:08:31 +00:00
|
|
|
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
|
|
|
|
}
|
2024-07-09 21:50:54 +00:00
|
|
|
|
|
|
|
// 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
|
2024-07-10 20:08:23 +00:00
|
|
|
room_id
|
2024-07-09 21:50:54 +00:00
|
|
|
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
|
|
|
|
}
|
2024-07-22 20:14:48 +00:00
|
|
|
defer rows.Close()
|
|
|
|
|
2024-07-09 21:50:54 +00:00
|
|
|
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
|
|
|
|
}
|
2024-07-10 20:34:27 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
2024-07-19 07:44:27 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
2024-07-22 20:14:48 +00:00
|
|
|
|
|
|
|
// 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,
|
2024-07-24 12:20:30 +00:00
|
|
|
r.updated_at, r.processed, rm.id, rm.room_name
|
2024-07-22 20:14:48 +00:00
|
|
|
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,
|
2024-07-24 12:20:30 +00:00
|
|
|
&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,
|
2024-07-22 20:14:48 +00:00
|
|
|
&i.Room.ID,
|
|
|
|
&i.Room.RoomName,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return reservations, err
|
|
|
|
}
|
|
|
|
reservations = append(reservations, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
return reservations, nil
|
|
|
|
}
|
2024-07-24 20:23:31 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
2024-07-25 11:13:16 +00:00
|
|
|
|
|
|
|
// UpdateReservation updates a user in the database
|
|
|
|
func (m *postgresDBRepo) UpdateReservation(r models.Reservation) error {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
2024-07-25 11:29:57 +00:00
|
|
|
query := `update reservations set first_name = $1, last_name = $2, email = $3, phone = $4, updated_at = $5
|
|
|
|
where id = $6`
|
2024-07-25 11:13:16 +00:00
|
|
|
|
2024-07-25 11:29:57 +00:00
|
|
|
_, err := m.DB.ExecContext(ctx, query, r.FirstName, r.LastName, r.Email,
|
|
|
|
r.Phone, time.Now(), r.ID)
|
2024-07-25 11:13:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteReservation deletes one reservation by ID
|
|
|
|
func (m *postgresDBRepo) DeleteReservation(id int) error {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
query := `delete from reservations where id = $1`
|
|
|
|
|
|
|
|
_, err := m.DB.ExecContext(ctx, query, id)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateProcessedForReservation set processed for a reservation
|
|
|
|
func (m *postgresDBRepo) UpdateProcessedForReservation(id, processed int) error {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
query := `update reservations set processed = $1 where id = $2`
|
|
|
|
|
|
|
|
_, err := m.DB.ExecContext(ctx, query, processed, id)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2024-07-26 11:40:56 +00:00
|
|
|
|
2024-07-27 20:39:23 +00:00
|
|
|
// AllRooms gets all rooms
|
2024-07-26 11:40:56 +00:00
|
|
|
func (m *postgresDBRepo) AllRooms() ([]models.Room, error) {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
var rooms []models.Room
|
|
|
|
|
|
|
|
query := `select id, room_name, created_at, updated_at from rooms order by room_name`
|
|
|
|
|
|
|
|
rows, err := m.DB.QueryContext(ctx, query)
|
|
|
|
if err != nil {
|
|
|
|
return rooms, err
|
|
|
|
}
|
|
|
|
defer rows.Close() // To avoid memory leak
|
|
|
|
|
|
|
|
for rows.Next() {
|
|
|
|
var rm models.Room
|
|
|
|
err := rows.Scan(
|
|
|
|
&rm.ID,
|
|
|
|
&rm.RoomName,
|
|
|
|
&rm.CreatedAt,
|
|
|
|
&rm.UpdatedAt,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return rooms, err
|
|
|
|
}
|
|
|
|
rooms = append(rooms, rm)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = rows.Err(); err != nil {
|
|
|
|
return rooms, err
|
|
|
|
}
|
|
|
|
return rooms, nil
|
|
|
|
}
|
2024-07-27 20:39:23 +00:00
|
|
|
|
|
|
|
// GetRestrictionsForRoomByDate returns restrictions for a room by date range
|
|
|
|
func (m *postgresDBRepo) GetRestrictionsForRoomByDate(roomId int, start, end time.Time) ([]models.RoomRestriction, error) {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
var restrictions []models.RoomRestriction
|
|
|
|
|
|
|
|
// coalesce use 0 if null
|
|
|
|
query := `select id, coalesce(reservation_id, 0), restriction_id, room_id, start_date, end_date
|
|
|
|
from room_restrictions where $1 < end_date and $2 >= start_date
|
|
|
|
and room_id = $3`
|
|
|
|
|
|
|
|
rows, err := m.DB.QueryContext(ctx, query, start, end, roomId)
|
|
|
|
if err != nil {
|
|
|
|
return restrictions, err
|
|
|
|
}
|
|
|
|
defer rows.Close() // To avoid memory leak
|
|
|
|
|
|
|
|
for rows.Next() {
|
|
|
|
var r models.RoomRestriction
|
|
|
|
err := rows.Scan(
|
|
|
|
&r.ID,
|
|
|
|
&r.ReservationID,
|
|
|
|
&r.RestrictionID,
|
|
|
|
&r.RoomID,
|
|
|
|
&r.StartDate,
|
|
|
|
&r.EndDate,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return restrictions, err
|
|
|
|
}
|
|
|
|
restrictions = append(restrictions, r)
|
|
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
|
|
return restrictions, err
|
|
|
|
}
|
|
|
|
return restrictions, nil
|
|
|
|
}
|
2024-07-28 12:43:39 +00:00
|
|
|
|
|
|
|
// InsertBlockForRoom inserts a room restriction
|
|
|
|
func (m *postgresDBRepo) InsertBlockForRoom(id int, startDate time.Time) error {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
query := `insert into room_restrictions (start_date, end_date, room_id, restriction_id,
|
|
|
|
created_at, updated_at)
|
|
|
|
values ($1, $2, $3, $4, $5, $6)`
|
|
|
|
|
|
|
|
_, err := m.DB.ExecContext(ctx, query, startDate, startDate.AddDate(0, 0, 1), id, 2, time.Now(), time.Now())
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteBlockByID deletes a block by ID
|
|
|
|
func (m *postgresDBRepo) DeleteBlockByID(id int) error {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
query := `delete from room_restrictions where id = $1`
|
|
|
|
|
|
|
|
_, err := m.DB.ExecContext(ctx, query, id)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|