feat: Go backend, enhanced search, new widgets, Docker deploy

Major changes:
- Add Go backend (backend/) with microservices architecture
- Enhanced master-agents-svc: reranker, content-classifier, stealth-crawler,
  proxy-manager, media-search, fastClassifier, language detection
- New web-svc widgets: KnowledgeCard, ProductCard, ProfileCard, VideoCard,
  UnifiedCard, CardGallery, InlineImageGallery, SourcesPanel, RelatedQuestions
- Improved discover-svc with discover-db integration
- Docker deployment improvements (Caddyfile, vendor.sh, BUILD.md)
- Library-svc: project_id schema migration
- Remove deprecated finance-svc and travel-svc
- Localization improvements across services

Made-with: Cursor
This commit is contained in:
home
2026-02-27 04:15:32 +03:00
parent 328d968f3f
commit 06fe57c765
285 changed files with 53132 additions and 1871 deletions

View File

@@ -0,0 +1,215 @@
package connectors
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net/smtp"
"strings"
)
type EmailConfig struct {
SMTPHost string
SMTPPort int
Username string
Password string
FromAddress string
FromName string
UseTLS bool
AllowHTML bool
}
type EmailConnector struct {
cfg EmailConfig
}
func NewEmailConnector(cfg EmailConfig) *EmailConnector {
return &EmailConnector{cfg: cfg}
}
func (e *EmailConnector) ID() string {
return "email"
}
func (e *EmailConnector) Name() string {
return "Email"
}
func (e *EmailConnector) Description() string {
return "Send emails via SMTP"
}
func (e *EmailConnector) GetActions() []Action {
return []Action{
{
Name: "send",
Description: "Send an email",
Schema: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"to": map[string]interface{}{"type": "string", "description": "Recipient email address"},
"subject": map[string]interface{}{"type": "string", "description": "Email subject"},
"body": map[string]interface{}{"type": "string", "description": "Email body"},
"html": map[string]interface{}{"type": "boolean", "description": "Whether body is HTML"},
"cc": map[string]interface{}{"type": "string", "description": "CC recipients (comma-separated)"},
"bcc": map[string]interface{}{"type": "string", "description": "BCC recipients (comma-separated)"},
},
},
Required: []string{"to", "subject", "body"},
},
}
}
func (e *EmailConnector) Validate(params map[string]interface{}) error {
if _, ok := params["to"]; !ok {
return errors.New("'to' is required")
}
if _, ok := params["subject"]; !ok {
return errors.New("'subject' is required")
}
if _, ok := params["body"]; !ok {
return errors.New("'body' is required")
}
return nil
}
func (e *EmailConnector) Execute(ctx context.Context, action string, params map[string]interface{}) (interface{}, error) {
switch action {
case "send":
return e.send(ctx, params)
default:
return nil, errors.New("unknown action: " + action)
}
}
func (e *EmailConnector) send(ctx context.Context, params map[string]interface{}) (interface{}, error) {
to := params["to"].(string)
subject := params["subject"].(string)
body := params["body"].(string)
isHTML := false
if html, ok := params["html"].(bool); ok {
isHTML = html && e.cfg.AllowHTML
}
var cc, bcc []string
if ccStr, ok := params["cc"].(string); ok && ccStr != "" {
cc = strings.Split(ccStr, ",")
for i := range cc {
cc[i] = strings.TrimSpace(cc[i])
}
}
if bccStr, ok := params["bcc"].(string); ok && bccStr != "" {
bcc = strings.Split(bccStr, ",")
for i := range bcc {
bcc[i] = strings.TrimSpace(bcc[i])
}
}
from := e.cfg.FromAddress
if e.cfg.FromName != "" {
from = fmt.Sprintf("%s <%s>", e.cfg.FromName, e.cfg.FromAddress)
}
var msg strings.Builder
msg.WriteString(fmt.Sprintf("From: %s\r\n", from))
msg.WriteString(fmt.Sprintf("To: %s\r\n", to))
if len(cc) > 0 {
msg.WriteString(fmt.Sprintf("Cc: %s\r\n", strings.Join(cc, ", ")))
}
msg.WriteString(fmt.Sprintf("Subject: %s\r\n", subject))
msg.WriteString("MIME-Version: 1.0\r\n")
if isHTML {
msg.WriteString("Content-Type: text/html; charset=\"UTF-8\"\r\n")
} else {
msg.WriteString("Content-Type: text/plain; charset=\"UTF-8\"\r\n")
}
msg.WriteString("\r\n")
msg.WriteString(body)
recipients := []string{to}
recipients = append(recipients, cc...)
recipients = append(recipients, bcc...)
addr := fmt.Sprintf("%s:%d", e.cfg.SMTPHost, e.cfg.SMTPPort)
var auth smtp.Auth
if e.cfg.Username != "" && e.cfg.Password != "" {
auth = smtp.PlainAuth("", e.cfg.Username, e.cfg.Password, e.cfg.SMTPHost)
}
var err error
if e.cfg.UseTLS {
err = e.sendWithTLS(addr, auth, e.cfg.FromAddress, recipients, []byte(msg.String()))
} else {
err = smtp.SendMail(addr, auth, e.cfg.FromAddress, recipients, []byte(msg.String()))
}
if err != nil {
return map[string]interface{}{
"success": false,
"error": err.Error(),
}, err
}
return map[string]interface{}{
"success": true,
"to": to,
"subject": subject,
"recipients": len(recipients),
}, nil
}
func (e *EmailConnector) sendWithTLS(addr string, auth smtp.Auth, from string, to []string, msg []byte) error {
tlsConfig := &tls.Config{
ServerName: e.cfg.SMTPHost,
}
conn, err := tls.Dial("tcp", addr, tlsConfig)
if err != nil {
return err
}
defer conn.Close()
client, err := smtp.NewClient(conn, e.cfg.SMTPHost)
if err != nil {
return err
}
defer client.Close()
if auth != nil {
if err := client.Auth(auth); err != nil {
return err
}
}
if err := client.Mail(from); err != nil {
return err
}
for _, recipient := range to {
if err := client.Rcpt(recipient); err != nil {
return err
}
}
w, err := client.Data()
if err != nil {
return err
}
_, err = w.Write(msg)
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
return client.Quit()
}