mirror of
https://github.com/lone-cloud/prism
synced 2026-06-03 08:43:10 -07:00
205 lines
6 KiB
Go
205 lines
6 KiB
Go
package integration
|
|
|
|
import (
|
|
"context"
|
|
"embed"
|
|
"fmt"
|
|
"html/template"
|
|
"io/fs"
|
|
"log/slog"
|
|
"net/http"
|
|
|
|
"prism/service/config"
|
|
"prism/service/delivery"
|
|
"prism/service/integration/proton"
|
|
"prism/service/integration/signal"
|
|
"prism/service/integration/telegram"
|
|
"prism/service/integration/webpush"
|
|
"prism/service/subscription"
|
|
"prism/service/util"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
//go:embed templates/*.html
|
|
var templates embed.FS
|
|
|
|
type Integration interface {
|
|
RegisterRoutes(router *chi.Mux, auth func(http.Handler) http.Handler, logger *slog.Logger)
|
|
Start(ctx context.Context, logger *slog.Logger)
|
|
IsEnabled() bool
|
|
Health() (linked bool, account string)
|
|
}
|
|
|
|
type AutoSubscriber interface {
|
|
AutoSubscribe(appName string, store *subscription.Store, publisher *delivery.Publisher) error
|
|
}
|
|
|
|
type Integrations struct {
|
|
Publisher *delivery.Publisher
|
|
Signal *signal.Integration
|
|
Telegram *telegram.Integration
|
|
Proton *proton.Integration
|
|
store *subscription.Store
|
|
logger *slog.Logger
|
|
integrations []Integration
|
|
}
|
|
|
|
func Initialize(cfg *config.Config, store *subscription.Store, logger *slog.Logger, baseTmpl *template.Template) (*Integrations, *template.Template, error) {
|
|
var err error
|
|
baseTmpl, err = baseTmpl.ParseFS(templates, "templates/*.html")
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to parse integration templates: %w", err)
|
|
}
|
|
|
|
var signalIntegration *signal.Integration
|
|
var telegramIntegration *telegram.Integration
|
|
var protonIntegration *proton.Integration
|
|
|
|
integrations := []Integration{webpush.NewIntegration(store, logger)}
|
|
|
|
if cfg.EnableSignal {
|
|
tmplRenderer, err := newIntegrationRenderer(baseTmpl, "signal", signal.Templates)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
signalIntegration = signal.NewIntegration(cfg, store, logger, tmplRenderer)
|
|
integrations = append(integrations, signalIntegration)
|
|
}
|
|
|
|
if cfg.EnableTelegram {
|
|
tmplRenderer, err := newIntegrationRenderer(baseTmpl, "telegram", telegram.Templates)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
telegramIntegration = telegram.NewIntegration(store, logger, tmplRenderer, cfg.APIKey)
|
|
integrations = append(integrations, telegramIntegration)
|
|
}
|
|
|
|
if cfg.EnableProton {
|
|
tmplRenderer, err := newIntegrationRenderer(baseTmpl, "proton", proton.Templates)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
protonIntegration = proton.NewIntegration(cfg, logger, tmplRenderer, store.DB, cfg.APIKey)
|
|
integrations = append(integrations, protonIntegration)
|
|
}
|
|
|
|
i := &Integrations{
|
|
Signal: signalIntegration,
|
|
Telegram: telegramIntegration,
|
|
Proton: protonIntegration,
|
|
store: store,
|
|
logger: logger,
|
|
integrations: integrations,
|
|
}
|
|
|
|
publisher := delivery.NewPublisher(store, logger, i.autoSubscribeApp)
|
|
publisher.RegisterSender(subscription.ChannelWebPush, webpush.NewSender(logger))
|
|
if signalIntegration != nil && signalIntegration.Sender != nil {
|
|
if linked, err := signalIntegration.Sender.IsLinked(); err == nil && linked {
|
|
publisher.RegisterSender(subscription.ChannelSignal, signalIntegration.Sender)
|
|
}
|
|
}
|
|
if telegramIntegration != nil && telegramIntegration.Sender != nil {
|
|
publisher.RegisterSender(subscription.ChannelTelegram, telegramIntegration.Sender)
|
|
}
|
|
i.Publisher = publisher
|
|
|
|
if telegramIntegration != nil {
|
|
telegramIntegration.OnLink = func() {
|
|
client := telegramIntegration.Handlers.GetClient()
|
|
chatID := telegramIntegration.Handlers.GetChatID()
|
|
if client != nil && chatID != 0 {
|
|
sender := telegram.NewSender(client, store, logger, chatID)
|
|
telegramIntegration.Sender = sender
|
|
i.Publisher.RegisterSender(subscription.ChannelTelegram, sender)
|
|
}
|
|
}
|
|
telegramIntegration.OnUnlink = func() {
|
|
i.Publisher.DeregisterSender(subscription.ChannelTelegram)
|
|
if err := i.store.DeleteSubscriptionsByChannel(subscription.ChannelTelegram); err != nil {
|
|
i.logger.Error("Failed to delete Telegram subscriptions on unlink", "error", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if protonIntegration != nil {
|
|
protonIntegration.Publisher = publisher
|
|
}
|
|
|
|
return i, baseTmpl, nil
|
|
}
|
|
|
|
func newIntegrationRenderer(base *template.Template, name string, templates fs.FS) (*util.TemplateRenderer, error) {
|
|
integrationTmpl, err := base.Clone()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to clone base templates for %s: %w", name, err)
|
|
}
|
|
integrationTmpl, err = integrationTmpl.ParseFS(templates, "templates/*.html")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s templates: %w", name, err)
|
|
}
|
|
|
|
return util.NewTemplateRenderer(integrationTmpl), nil
|
|
}
|
|
|
|
func (i *Integrations) Start(ctx context.Context, logger *slog.Logger) {
|
|
for _, integration := range i.integrations {
|
|
if integration.IsEnabled() {
|
|
integration.Start(ctx, logger)
|
|
}
|
|
}
|
|
}
|
|
|
|
func RegisterAll(integrations *Integrations, router *chi.Mux, cfg *config.Config, logger *slog.Logger, authMiddleware func(string) func(http.Handler) http.Handler) {
|
|
auth := authMiddleware(cfg.APIKey)
|
|
for _, integration := range integrations.integrations {
|
|
integration.RegisterRoutes(router, auth, logger)
|
|
}
|
|
}
|
|
|
|
func (i *Integrations) autoSubscribeApp(appName string) error {
|
|
app, err := i.store.GetApp(appName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if app != nil {
|
|
return nil
|
|
}
|
|
|
|
if err := i.store.RegisterApp(appName); err != nil {
|
|
return fmt.Errorf("failed to register app %q: %w", appName, err)
|
|
}
|
|
i.logger.Info("Registering new app and auto-configuring subscription", "app", appName)
|
|
|
|
for _, integration := range i.integrations {
|
|
sub, ok := integration.(AutoSubscriber)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if err := sub.AutoSubscribe(appName, i.store, i.Publisher); err == nil {
|
|
return nil
|
|
} else {
|
|
i.logger.Warn("Auto-config failed", "integration", fmt.Sprintf("%T", integration), "app", appName, "error", err)
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("no integration available to auto-configure app %q", appName)
|
|
}
|
|
|
|
func (i *Integrations) IsSignalLinked() bool {
|
|
if i.Signal == nil {
|
|
return false
|
|
}
|
|
linked, _ := i.Signal.Health()
|
|
return linked
|
|
}
|
|
|
|
func (i *Integrations) IsTelegramLinked() bool {
|
|
if i.Telegram == nil {
|
|
return false
|
|
}
|
|
linked, _ := i.Telegram.Health()
|
|
return linked
|
|
}
|