Add Next.js admin panel and implement background worker jobs
- Add full Next.js admin panel with: - User, residence, task, contractor, document management - Notifications and notification preferences management - Subscriptions and auth token management - Dashboard with stats - Lookup tables management (categories, priorities, statuses, etc.) - Admin user management - Implement background worker job handlers: - HandleTaskReminder: sends push notifications for tasks due within 24h - HandleOverdueReminder: sends push notifications for overdue tasks - HandleDailyDigest: sends daily summary of pending tasks - HandleSendEmail: processes email sending jobs - HandleSendPush: processes push notification jobs - Make worker job schedules configurable via environment variables: - TASK_REMINDER_HOUR, TASK_REMINDER_MINUTE (default: 20:00 UTC) - OVERDUE_REMINDER_HOUR (default: 09:00 UTC) - DAILY_DIGEST_HOUR (default: 11:00 UTC) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ type Config struct {
|
||||
Push PushConfig
|
||||
Worker WorkerConfig
|
||||
Security SecurityConfig
|
||||
Storage StorageConfig
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
@@ -87,6 +88,14 @@ type SecurityConfig struct {
|
||||
MaxPasswordResetRate int // per hour
|
||||
}
|
||||
|
||||
// StorageConfig holds file storage settings
|
||||
type StorageConfig struct {
|
||||
UploadDir string // Directory to store uploaded files
|
||||
BaseURL string // Public URL prefix for serving files (e.g., "/uploads")
|
||||
MaxFileSize int64 // Max file size in bytes (default 10MB)
|
||||
AllowedTypes string // Comma-separated MIME types
|
||||
}
|
||||
|
||||
var cfg *Config
|
||||
|
||||
// Load reads configuration from environment variables
|
||||
@@ -157,10 +166,10 @@ func Load() (*Config, error) {
|
||||
FCMServerKey: viper.GetString("FCM_SERVER_KEY"),
|
||||
},
|
||||
Worker: WorkerConfig{
|
||||
TaskReminderHour: viper.GetInt("CELERY_BEAT_REMINDER_HOUR"),
|
||||
TaskReminderMinute: viper.GetInt("CELERY_BEAT_REMINDER_MINUTE"),
|
||||
OverdueReminderHour: 9, // 9:00 AM UTC
|
||||
DailyNotifHour: 11, // 11:00 AM UTC
|
||||
TaskReminderHour: viper.GetInt("TASK_REMINDER_HOUR"),
|
||||
TaskReminderMinute: viper.GetInt("TASK_REMINDER_MINUTE"),
|
||||
OverdueReminderHour: viper.GetInt("OVERDUE_REMINDER_HOUR"),
|
||||
DailyNotifHour: viper.GetInt("DAILY_DIGEST_HOUR"),
|
||||
},
|
||||
Security: SecurityConfig{
|
||||
SecretKey: viper.GetString("SECRET_KEY"),
|
||||
@@ -169,6 +178,12 @@ func Load() (*Config, error) {
|
||||
ConfirmationExpiry: 24 * time.Hour,
|
||||
MaxPasswordResetRate: 3,
|
||||
},
|
||||
Storage: StorageConfig{
|
||||
UploadDir: viper.GetString("STORAGE_UPLOAD_DIR"),
|
||||
BaseURL: viper.GetString("STORAGE_BASE_URL"),
|
||||
MaxFileSize: viper.GetInt64("STORAGE_MAX_FILE_SIZE"),
|
||||
AllowedTypes: viper.GetString("STORAGE_ALLOWED_TYPES"),
|
||||
},
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
@@ -218,9 +233,17 @@ func setDefaults() {
|
||||
viper.SetDefault("APNS_TOPIC", "com.example.mycrib")
|
||||
viper.SetDefault("APNS_USE_SANDBOX", true)
|
||||
|
||||
// Worker defaults
|
||||
viper.SetDefault("CELERY_BEAT_REMINDER_HOUR", 20)
|
||||
viper.SetDefault("CELERY_BEAT_REMINDER_MINUTE", 0)
|
||||
// Worker defaults (all times in UTC)
|
||||
viper.SetDefault("TASK_REMINDER_HOUR", 20) // 8:00 PM UTC
|
||||
viper.SetDefault("TASK_REMINDER_MINUTE", 0)
|
||||
viper.SetDefault("OVERDUE_REMINDER_HOUR", 9) // 9:00 AM UTC
|
||||
viper.SetDefault("DAILY_DIGEST_HOUR", 11) // 11:00 AM UTC
|
||||
|
||||
// Storage defaults
|
||||
viper.SetDefault("STORAGE_UPLOAD_DIR", "./uploads")
|
||||
viper.SetDefault("STORAGE_BASE_URL", "/uploads")
|
||||
viper.SetDefault("STORAGE_MAX_FILE_SIZE", 10*1024*1024) // 10MB
|
||||
viper.SetDefault("STORAGE_ALLOWED_TYPES", "image/jpeg,image/png,image/gif,image/webp,application/pdf")
|
||||
}
|
||||
|
||||
func validate(cfg *Config) error {
|
||||
|
||||
Reference in New Issue
Block a user