mirror of
https://github.com/ente-io/ente.git
synced 2025-07-10 00:54:13 +00:00
173 lines
7.0 KiB
Go
173 lines
7.0 KiB
Go
package emergency
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/ente-io/museum/ente"
|
|
"github.com/ente-io/museum/pkg/utils/time"
|
|
"github.com/ente-io/stacktrace"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/lib/pq"
|
|
)
|
|
|
|
type RecoverRow struct {
|
|
ID uuid.UUID
|
|
UserID int64
|
|
EmergencyContactID int64
|
|
Status ente.RecoveryStatus
|
|
WaitTill int64
|
|
NextReminderAt int64
|
|
CreatedAt int64
|
|
}
|
|
|
|
func (r RecoverRow) CanRecover() error {
|
|
if r.Status != ente.RecoveryStatusReady && r.Status != ente.RecoveryStatusWaiting {
|
|
return fmt.Errorf("recovery status is not waiting or ready")
|
|
}
|
|
if r.WaitTill > time.Microseconds() && r.Status == ente.RecoveryStatusWaiting {
|
|
return fmt.Errorf("recovery wait time is not over")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (repo *Repository) InsertIntoRecovery(ctx *gin.Context, contact ente.ContactIdentifier, contactRow ContactRow) (bool, error) {
|
|
if contactRow.NoticePeriodInHrs <= 24 {
|
|
logrus.Warn("notice period is less than 24 hours")
|
|
return false, ente.NewBadRequestWithMessage("notice period should be greater than 24 hours")
|
|
}
|
|
waitTime := time.MicrosecondsAfterHours(contactRow.NoticePeriodInHrs)
|
|
// remind after 7 days.
|
|
nextReminder := time.MicrosecondsAfterHours(24 * 7)
|
|
if nextReminder >= waitTime {
|
|
logrus.Warn("initial reminder is greater than wait time")
|
|
// remind in 1 day if notice period is less than 7 days.
|
|
nextReminder = time.MicrosecondsAfterHours(24 * 1)
|
|
}
|
|
result, err := repo.DB.ExecContext(ctx, `INSERT INTO emergency_recovery (id,user_id, emergency_contact_id, status, wait_till, next_reminder_at) VALUES ($1, $2, $3, $4, $5, $6) on conflict DO NOTHING`,
|
|
uuid.New(), contact.UserID, contact.EmergencyContactID, ente.RecoveryStatusWaiting, waitTime, nextReminder)
|
|
if err != nil {
|
|
return false, stacktrace.Propagate(err, "")
|
|
}
|
|
count, _ := result.RowsAffected()
|
|
return count > 0, nil
|
|
}
|
|
|
|
func (repo *Repository) GetActiveRecoverySessions(ctx *gin.Context, userID int64) ([]*RecoverRow, error) {
|
|
rows, err := repo.DB.QueryContext(ctx, `SELECT id, user_id, emergency_contact_id, status, wait_till, next_reminder_at, created_at
|
|
FROM emergency_recovery WHERE (user_id=$1 OR emergency_contact_id=$1) AND status= ANY($2)`, userID, pq.Array([]ente.RecoveryStatus{ente.RecoveryStatusWaiting, ente.RecoveryStatusReady}))
|
|
if err != nil {
|
|
return nil, stacktrace.Propagate(err, "")
|
|
}
|
|
defer rows.Close()
|
|
var sessions []*RecoverRow
|
|
for rows.Next() {
|
|
var row RecoverRow
|
|
if err := rows.Scan(&row.ID, &row.UserID, &row.EmergencyContactID, &row.Status, &row.WaitTill, &row.NextReminderAt, &row.CreatedAt); err != nil {
|
|
return nil, stacktrace.Propagate(err, "")
|
|
}
|
|
sessions = append(sessions, &row)
|
|
}
|
|
return sessions, nil
|
|
}
|
|
|
|
func (repo *Repository) GetActiveSessions(ctx *gin.Context, userID int64, emergencyContactID int64) ([]*RecoverRow, error) {
|
|
rows, err := repo.DB.QueryContext(ctx, `SELECT id, user_id, emergency_contact_id, status, wait_till, next_reminder_at, created_at
|
|
FROM emergency_recovery WHERE user_id=$1 and emergency_contact_id=$2 AND status= ANY($3)`, userID, emergencyContactID, pq.Array([]ente.RecoveryStatus{ente.RecoveryStatusWaiting, ente.RecoveryStatusReady}))
|
|
if err != nil {
|
|
return nil, stacktrace.Propagate(err, "")
|
|
}
|
|
defer rows.Close()
|
|
var sessions []*RecoverRow
|
|
for rows.Next() {
|
|
var row RecoverRow
|
|
if err := rows.Scan(&row.ID, &row.UserID, &row.EmergencyContactID, &row.Status, &row.WaitTill, &row.NextReminderAt, &row.CreatedAt); err != nil {
|
|
return nil, stacktrace.Propagate(err, "")
|
|
}
|
|
sessions = append(sessions, &row)
|
|
}
|
|
return sessions, nil
|
|
}
|
|
|
|
func (r *Repository) GetActiveRecoveryForNotification() (*[]RecoverRow, error) {
|
|
rows, err := r.DB.Query(`
|
|
SELECT id, user_id, emergency_contact_id, status, wait_till, next_reminder_at, created_at
|
|
FROM emergency_recovery WHERE (status = $1) and next_reminder_at < now_utc_micro_seconds()`, ente.RecoveryStatusWaiting)
|
|
if err != nil {
|
|
return nil, stacktrace.Propagate(err, "")
|
|
}
|
|
defer rows.Close()
|
|
var sessions []RecoverRow
|
|
for rows.Next() {
|
|
var row RecoverRow
|
|
if err := rows.Scan(&row.ID, &row.UserID, &row.EmergencyContactID, &row.Status, &row.WaitTill, &row.NextReminderAt, &row.CreatedAt); err != nil {
|
|
return nil, stacktrace.Propagate(err, "")
|
|
}
|
|
sessions = append(sessions, row)
|
|
}
|
|
return &sessions, nil
|
|
}
|
|
|
|
func (r *Repository) UpdateNextReminder(ctx context.Context, sessionID uuid.UUID, nextReminder int64) error {
|
|
_, err := r.DB.ExecContext(ctx, `UPDATE emergency_recovery SET next_reminder_at=$1 WHERE id=$2`, nextReminder, sessionID)
|
|
if err != nil {
|
|
return stacktrace.Propagate(err, "")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (repo *Repository) UpdateRecoveryStatusForID(ctx context.Context, sessionID uuid.UUID, status ente.RecoveryStatus) (bool, error) {
|
|
validPrevStatus := validPreviousStatus(status)
|
|
var result sql.Result
|
|
var err error
|
|
if status == ente.RecoveryStatusReady {
|
|
result, err = repo.DB.ExecContext(ctx, `UPDATE emergency_recovery SET status=$1, wait_till=$2 WHERE id=$3 and status = ANY($4)`, status, time.Microseconds(), sessionID, pq.Array(validPrevStatus))
|
|
} else {
|
|
result, err = repo.DB.ExecContext(ctx, `UPDATE emergency_recovery SET status=$1 WHERE id=$2 and status = ANY($3)`, status, sessionID, pq.Array(validPrevStatus))
|
|
}
|
|
if err != nil {
|
|
return false, stacktrace.Propagate(err, "")
|
|
}
|
|
rows, _ := result.RowsAffected()
|
|
return rows > 0, nil
|
|
}
|
|
|
|
func (repo *Repository) GetRecoverRowByID(ctx context.Context, sessionID uuid.UUID) (*RecoverRow, error) {
|
|
var row RecoverRow
|
|
err := repo.DB.QueryRowContext(ctx, `SELECT id, user_id, emergency_contact_id, status, wait_till, next_reminder_at, created_at
|
|
FROM emergency_recovery WHERE id=$1`, sessionID).Scan(&row.ID, &row.UserID, &row.EmergencyContactID, &row.Status, &row.WaitTill, &row.NextReminderAt, &row.CreatedAt)
|
|
if err != nil {
|
|
return nil, stacktrace.Propagate(err, "")
|
|
}
|
|
return &row, nil
|
|
}
|
|
|
|
func (repo *Repository) UpdateRecoveryStatus(ctx context.Context, userID, emergencyContactID int64, status ente.RecoveryStatus) error {
|
|
validPrevStatus := validPreviousStatus(status)
|
|
_, err := repo.DB.ExecContext(ctx, `UPDATE emergency_recovery SET status=$1 WHERE user_id =$2 and emergency_contact_id =$3 and status = ANY($4)`, status, userID, emergencyContactID, pq.Array(validPrevStatus))
|
|
if err != nil {
|
|
return stacktrace.Propagate(err, "")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validPreviousStatus(newStatus ente.RecoveryStatus) []ente.RecoveryStatus {
|
|
result := make([]ente.RecoveryStatus, 0)
|
|
switch newStatus {
|
|
case ente.RecoveryStatusWaiting:
|
|
break
|
|
case ente.RecoveryStatusReady:
|
|
result = append(result, ente.RecoveryStatusWaiting, ente.RecoveryStatusReady)
|
|
case ente.RecoveryStatusStopped:
|
|
result = append(result, ente.RecoveryStatusWaiting, ente.RecoveryStatusReady)
|
|
case ente.RecoveryStatusRejected:
|
|
result = append(result, ente.RecoveryStatusWaiting, ente.RecoveryStatusReady)
|
|
case ente.RecoveryStatusRecovered:
|
|
result = append(result, ente.RecoveryStatusWaiting, ente.RecoveryStatusReady)
|
|
}
|
|
return result
|
|
}
|