- Backend: Email producer (LavinMQ), EmailService interface - Backend: CRUD API for email_templates and email_settings - Backend: avatar_url field in users table + UpdateMyProfile support - Backend: StorageService for pre-signed URLs - NestJS: Email consumer with Nodemailer and Handlebars - Frontend: Email Templates admin pages (list/edit) - Frontend: Updated profileApi.uploadAvatar with pre-signed URL flow - Frontend: New /post-job public page (company registration + job creation wizard) - Migrations: 027_create_email_system.sql, 028_add_avatar_url_to_users.sql
64 lines
1.6 KiB
Go
64 lines
1.6 KiB
Go
package services
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
)
|
|
|
|
type CloudflareConfig struct {
|
|
APIToken string `json:"apiToken"`
|
|
ZoneID string `json:"zoneId"`
|
|
}
|
|
|
|
type CloudflareService struct {
|
|
credentials *CredentialsService
|
|
}
|
|
|
|
func NewCloudflareService(c *CredentialsService) *CloudflareService {
|
|
return &CloudflareService{credentials: c}
|
|
}
|
|
|
|
// PurgeCache clears the entire Cloudflare cache for the configured zone
|
|
func (s *CloudflareService) PurgeCache(ctx context.Context) error {
|
|
payload, err := s.credentials.GetDecryptedKey(ctx, "cloudflare_config")
|
|
if err != nil {
|
|
return fmt.Errorf("cloudflare credentials missing: %w", err)
|
|
}
|
|
|
|
var cfg CloudflareConfig
|
|
if err := json.Unmarshal([]byte(payload), &cfg); err != nil {
|
|
return fmt.Errorf("invalid cloudflare config: %w", err)
|
|
}
|
|
|
|
if cfg.APIToken == "" || cfg.ZoneID == "" {
|
|
return fmt.Errorf("cloudflare not configured")
|
|
}
|
|
|
|
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/purge_cache", cfg.ZoneID)
|
|
body := []byte(`{"purge_everything":true}`)
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(body))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req.Header.Set("Authorization", "Bearer "+cfg.APIToken)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("cloudflare api request failed: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
respBody, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("cloudflare api returned %d: %s", resp.StatusCode, string(respBody))
|
|
}
|
|
|
|
return nil
|
|
}
|