Complete rewrite of Django REST API to Go with: - Gin web framework for HTTP routing - GORM for database operations - GoAdmin for admin panel - Gorush integration for push notifications - Redis for caching and job queues Features implemented: - User authentication (login, register, logout, password reset) - Residence management (CRUD, sharing, share codes) - Task management (CRUD, kanban board, completions) - Contractor management (CRUD, specialties) - Document management (CRUD, warranties) - Notifications (preferences, push notifications) - Subscription management (tiers, limits) Infrastructure: - Docker Compose for local development - Database migrations and seed data - Admin panel for data management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
163 lines
4.3 KiB
Go
163 lines
4.3 KiB
Go
package jobs
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"github.com/hibiken/asynq"
|
|
"github.com/rs/zerolog/log"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/treytartt/mycrib-api/internal/config"
|
|
"github.com/treytartt/mycrib-api/internal/push"
|
|
)
|
|
|
|
// Task types
|
|
const (
|
|
TypeTaskReminder = "notification:task_reminder"
|
|
TypeOverdueReminder = "notification:overdue_reminder"
|
|
TypeDailyDigest = "notification:daily_digest"
|
|
TypeSendEmail = "email:send"
|
|
TypeSendPush = "push:send"
|
|
)
|
|
|
|
// Handler handles background job processing
|
|
type Handler struct {
|
|
db *gorm.DB
|
|
pushClient *push.GorushClient
|
|
config *config.Config
|
|
}
|
|
|
|
// NewHandler creates a new job handler
|
|
func NewHandler(db *gorm.DB, pushClient *push.GorushClient, cfg *config.Config) *Handler {
|
|
return &Handler{
|
|
db: db,
|
|
pushClient: pushClient,
|
|
config: cfg,
|
|
}
|
|
}
|
|
|
|
// HandleTaskReminder processes task reminder notifications
|
|
func (h *Handler) HandleTaskReminder(ctx context.Context, task *asynq.Task) error {
|
|
log.Info().Msg("Processing task reminder notifications...")
|
|
|
|
// TODO: Implement task reminder logic
|
|
// 1. Query tasks due today or tomorrow
|
|
// 2. Get user device tokens
|
|
// 3. Send push notifications via Gorush
|
|
|
|
log.Info().Msg("Task reminder notifications completed")
|
|
return nil
|
|
}
|
|
|
|
// HandleOverdueReminder processes overdue task notifications
|
|
func (h *Handler) HandleOverdueReminder(ctx context.Context, task *asynq.Task) error {
|
|
log.Info().Msg("Processing overdue task notifications...")
|
|
|
|
// TODO: Implement overdue reminder logic
|
|
// 1. Query overdue tasks
|
|
// 2. Get user device tokens
|
|
// 3. Send push notifications via Gorush
|
|
|
|
log.Info().Msg("Overdue task notifications completed")
|
|
return nil
|
|
}
|
|
|
|
// HandleDailyDigest processes daily digest notifications
|
|
func (h *Handler) HandleDailyDigest(ctx context.Context, task *asynq.Task) error {
|
|
log.Info().Msg("Processing daily digest notifications...")
|
|
|
|
// TODO: Implement daily digest logic
|
|
// 1. Aggregate task statistics per user
|
|
// 2. Get user device tokens
|
|
// 3. Send push notifications via Gorush
|
|
|
|
log.Info().Msg("Daily digest notifications completed")
|
|
return nil
|
|
}
|
|
|
|
// EmailPayload represents the payload for email tasks
|
|
type EmailPayload struct {
|
|
To string `json:"to"`
|
|
Subject string `json:"subject"`
|
|
Body string `json:"body"`
|
|
IsHTML bool `json:"is_html"`
|
|
}
|
|
|
|
// HandleSendEmail processes email sending tasks
|
|
func (h *Handler) HandleSendEmail(ctx context.Context, task *asynq.Task) error {
|
|
var payload EmailPayload
|
|
if err := json.Unmarshal(task.Payload(), &payload); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info().
|
|
Str("to", payload.To).
|
|
Str("subject", payload.Subject).
|
|
Msg("Sending email...")
|
|
|
|
// TODO: Implement email sending via EmailService
|
|
|
|
log.Info().Str("to", payload.To).Msg("Email sent successfully")
|
|
return nil
|
|
}
|
|
|
|
// PushPayload represents the payload for push notification tasks
|
|
type PushPayload struct {
|
|
UserID uint `json:"user_id"`
|
|
Title string `json:"title"`
|
|
Message string `json:"message"`
|
|
Data map[string]string `json:"data,omitempty"`
|
|
}
|
|
|
|
// HandleSendPush processes push notification tasks
|
|
func (h *Handler) HandleSendPush(ctx context.Context, task *asynq.Task) error {
|
|
var payload PushPayload
|
|
if err := json.Unmarshal(task.Payload(), &payload); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info().
|
|
Uint("user_id", payload.UserID).
|
|
Str("title", payload.Title).
|
|
Msg("Sending push notification...")
|
|
|
|
if h.pushClient == nil {
|
|
log.Warn().Msg("Push client not configured, skipping notification")
|
|
return nil
|
|
}
|
|
|
|
// TODO: Get user device tokens and send via Gorush
|
|
|
|
log.Info().Uint("user_id", payload.UserID).Msg("Push notification sent successfully")
|
|
return nil
|
|
}
|
|
|
|
// NewSendEmailTask creates a new email sending task
|
|
func NewSendEmailTask(to, subject, body string, isHTML bool) (*asynq.Task, error) {
|
|
payload, err := json.Marshal(EmailPayload{
|
|
To: to,
|
|
Subject: subject,
|
|
Body: body,
|
|
IsHTML: isHTML,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return asynq.NewTask(TypeSendEmail, payload), nil
|
|
}
|
|
|
|
// NewSendPushTask creates a new push notification task
|
|
func NewSendPushTask(userID uint, title, message string, data map[string]string) (*asynq.Task, error) {
|
|
payload, err := json.Marshal(PushPayload{
|
|
UserID: userID,
|
|
Title: title,
|
|
Message: message,
|
|
Data: data,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return asynq.NewTask(TypeSendPush, payload), nil
|
|
}
|