mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-11-10 12:20:10 +00:00
Compare commits
No commits in common. "9ae16163bb1b73d7a596ce07b3d4553d5d4502af" and "5ff38392392d9902f34bf119d99129c49e456a9b" have entirely different histories.
9ae16163bb
...
5ff3839239
159
email.go
159
email.go
@ -13,6 +13,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
textTemplate "text/template"
|
textTemplate "text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -30,6 +31,84 @@ type EmailClient interface {
|
|||||||
Send(fromName, fromAddr string, message *Message, address ...string) error
|
Send(fromName, fromAddr string, message *Message, address ...string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DummyClient struct{}
|
||||||
|
|
||||||
|
func (dc *DummyClient) Send(fromName, fromAddr string, email *Message, address ...string) error {
|
||||||
|
fmt.Printf("FROM: %s <%s>\nTO: %s\nTEXT: %s\n", fromName, fromAddr, strings.Join(address, ", "), email.Text)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mailgun client implements EmailClient.
|
||||||
|
type Mailgun struct {
|
||||||
|
client *mailgun.MailgunImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mg *Mailgun) Send(fromName, fromAddr string, email *Message, address ...string) error {
|
||||||
|
message := mg.client.NewMessage(
|
||||||
|
fmt.Sprintf("%s <%s>", fromName, fromAddr),
|
||||||
|
email.Subject,
|
||||||
|
email.Text,
|
||||||
|
)
|
||||||
|
for _, a := range address {
|
||||||
|
// Adding variable tells mailgun to do a batch send, so users don't see other recipients.
|
||||||
|
message.AddRecipientAndVariables(a, map[string]interface{}{"unique_id": a})
|
||||||
|
}
|
||||||
|
message.SetHtml(email.HTML)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||||
|
defer cancel()
|
||||||
|
_, _, err := mg.client.Send(ctx, message)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SMTP supports SSL/TLS and STARTTLS; implements EmailClient.
|
||||||
|
type SMTP struct {
|
||||||
|
Client *sMail.SMTPServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *SMTP) Send(fromName, fromAddr string, email *Message, address ...string) error {
|
||||||
|
from := fmt.Sprintf("%s <%s>", fromName, fromAddr)
|
||||||
|
var cli *sMail.SMTPClient
|
||||||
|
var err error
|
||||||
|
cli, err = sm.Client.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer cli.Close()
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for _, addr := range address {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(addr string) {
|
||||||
|
defer wg.Done()
|
||||||
|
e := sMail.NewMSG()
|
||||||
|
e.SetFrom(from)
|
||||||
|
e.SetSubject(email.Subject)
|
||||||
|
e.AddTo(addr)
|
||||||
|
if email.HTML == "" {
|
||||||
|
e.SetBody(sMail.TextPlain, email.Text)
|
||||||
|
} else {
|
||||||
|
e.SetBody(sMail.TextHTML, email.HTML)
|
||||||
|
e.AddAlternative(sMail.TextPlain, email.Text)
|
||||||
|
}
|
||||||
|
err = e.Send(cli)
|
||||||
|
// e := jEmail.NewEmail()
|
||||||
|
// e.Subject = email.Subject
|
||||||
|
// e.From = from
|
||||||
|
// e.Text = []byte(email.Text)
|
||||||
|
// e.HTML = []byte(email.HTML)
|
||||||
|
// e.To = []string{addr}
|
||||||
|
// err = sm.Pool.Send(e, 15*time.Second)
|
||||||
|
// if sm.sslTLS {
|
||||||
|
// err = sm.Pool.S
|
||||||
|
// err = e.SendWithTLS(server, sm.auth, sm.tlsConfig)
|
||||||
|
// } else {
|
||||||
|
// err = e.SendWithStartTLS(server, sm.auth, sm.tlsConfig)
|
||||||
|
// }
|
||||||
|
}(addr)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Emailer contains the email sender, translations, and methods to construct messages.
|
// Emailer contains the email sender, translations, and methods to construct messages.
|
||||||
type Emailer struct {
|
type Emailer struct {
|
||||||
fromAddr, fromName string
|
fromAddr, fromName string
|
||||||
@ -96,17 +175,18 @@ func NewEmailer(app *appContext) *Emailer {
|
|||||||
return emailer
|
return emailer
|
||||||
}
|
}
|
||||||
|
|
||||||
// DummyClient just logs the email to the console for debugging purposes. It can be used by settings [email]/method to "dummy".
|
// NewMailgun returns a Mailgun emailClient.
|
||||||
type DummyClient struct{}
|
func (emailer *Emailer) NewMailgun(url, key string) {
|
||||||
|
sender := &Mailgun{
|
||||||
func (dc *DummyClient) Send(fromName, fromAddr string, email *Message, address ...string) error {
|
client: mailgun.NewMailgun(strings.Split(emailer.fromAddr, "@")[1], key),
|
||||||
fmt.Printf("FROM: %s <%s>\nTO: %s\nTEXT: %s\n", fromName, fromAddr, strings.Join(address, ", "), email.Text)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
// Mailgun client takes the base url, so we need to trim off the end (e.g 'v3/messages')
|
||||||
// SMTP supports SSL/TLS and STARTTLS; implements EmailClient.
|
if strings.Contains(url, "messages") {
|
||||||
type SMTP struct {
|
url = url[0:strings.LastIndex(url, "/")]
|
||||||
Client *sMail.SMTPServer
|
url = url[0:strings.LastIndex(url, "/")]
|
||||||
|
}
|
||||||
|
sender.client.SetAPIBase(url)
|
||||||
|
emailer.sender = sender
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSMTP returns an SMTP emailClient.
|
// NewSMTP returns an SMTP emailClient.
|
||||||
@ -157,65 +237,6 @@ func (emailer *Emailer) NewSMTP(server string, port int, username, password stri
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *SMTP) Send(fromName, fromAddr string, email *Message, address ...string) error {
|
|
||||||
from := fmt.Sprintf("%s <%s>", fromName, fromAddr)
|
|
||||||
var cli *sMail.SMTPClient
|
|
||||||
var err error
|
|
||||||
cli, err = sm.Client.Connect()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer cli.Close()
|
|
||||||
e := sMail.NewMSG()
|
|
||||||
e.SetFrom(from)
|
|
||||||
e.SetSubject(email.Subject)
|
|
||||||
e.AddTo(address...)
|
|
||||||
if email.HTML == "" {
|
|
||||||
e.SetBody(sMail.TextPlain, email.Text)
|
|
||||||
} else {
|
|
||||||
e.SetBody(sMail.TextHTML, email.HTML)
|
|
||||||
e.AddAlternative(sMail.TextPlain, email.Text)
|
|
||||||
}
|
|
||||||
err = e.Send(cli)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mailgun client implements EmailClient.
|
|
||||||
type Mailgun struct {
|
|
||||||
client *mailgun.MailgunImpl
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMailgun returns a Mailgun emailClient.
|
|
||||||
func (emailer *Emailer) NewMailgun(url, key string) {
|
|
||||||
sender := &Mailgun{
|
|
||||||
client: mailgun.NewMailgun(strings.Split(emailer.fromAddr, "@")[1], key),
|
|
||||||
}
|
|
||||||
// Mailgun client takes the base url, so we need to trim off the end (e.g 'v3/messages')
|
|
||||||
if strings.Contains(url, "messages") {
|
|
||||||
url = url[0:strings.LastIndex(url, "/")]
|
|
||||||
url = url[0:strings.LastIndex(url, "/")]
|
|
||||||
}
|
|
||||||
sender.client.SetAPIBase(url)
|
|
||||||
emailer.sender = sender
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mg *Mailgun) Send(fromName, fromAddr string, email *Message, address ...string) error {
|
|
||||||
message := mg.client.NewMessage(
|
|
||||||
fmt.Sprintf("%s <%s>", fromName, fromAddr),
|
|
||||||
email.Subject,
|
|
||||||
email.Text,
|
|
||||||
)
|
|
||||||
for _, a := range address {
|
|
||||||
// Adding variable tells mailgun to do a batch send, so users don't see other recipients.
|
|
||||||
message.AddRecipientAndVariables(a, map[string]interface{}{"unique_id": a})
|
|
||||||
}
|
|
||||||
message.SetHtml(email.HTML)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
|
||||||
defer cancel()
|
|
||||||
_, _, err := mg.client.Send(ctx, message)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
type templ interface {
|
type templ interface {
|
||||||
Execute(wr io.Writer, data interface{}) error
|
Execute(wr io.Writer, data interface{}) error
|
||||||
}
|
}
|
||||||
|
@ -100,8 +100,7 @@
|
|||||||
"searchDiscordUser": "Gib den Discord-Benutzername ein, um den Benutzer zu finden.",
|
"searchDiscordUser": "Gib den Discord-Benutzername ein, um den Benutzer zu finden.",
|
||||||
"findDiscordUser": "Suche Discord-Benutzer",
|
"findDiscordUser": "Suche Discord-Benutzer",
|
||||||
"linkMatrixDescription": "Gib den Benutzernamen und das Passwort des Benutzers ein, der als Bot verwendet werden soll. Nach dem Absenden wird die App neu gestartet.",
|
"linkMatrixDescription": "Gib den Benutzernamen und das Passwort des Benutzers ein, der als Bot verwendet werden soll. Nach dem Absenden wird die App neu gestartet.",
|
||||||
"matrixHomeServer": "Adresse des Homeservers",
|
"matrixHomeServer": "Adresse des Homeservers"
|
||||||
"templates": "Vorlagen"
|
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"changedEmailAddress": "E-Mail-Adresse von {n} geändert.",
|
"changedEmailAddress": "E-Mail-Adresse von {n} geändert.",
|
||||||
@ -141,8 +140,7 @@
|
|||||||
"noUpdatesAvailable": "Keinen neuen Aktualisierungen verfügbar.",
|
"noUpdatesAvailable": "Keinen neuen Aktualisierungen verfügbar.",
|
||||||
"updateAppliedRefresh": "Update angewendet, bitte aktualisieren.",
|
"updateAppliedRefresh": "Update angewendet, bitte aktualisieren.",
|
||||||
"telegramVerified": "Telegram-Konto verifiziert.",
|
"telegramVerified": "Telegram-Konto verifiziert.",
|
||||||
"accountConnected": "Konto verbunden.",
|
"accountConnected": "Konto verbunden."
|
||||||
"savedAnnouncement": "Ankündigung gespeichert."
|
|
||||||
},
|
},
|
||||||
"quantityStrings": {
|
"quantityStrings": {
|
||||||
"modifySettingsFor": {
|
"modifySettingsFor": {
|
||||||
|
@ -101,11 +101,7 @@
|
|||||||
"findDiscordUser": "Trouver l'utilisateur Discord",
|
"findDiscordUser": "Trouver l'utilisateur Discord",
|
||||||
"linkMatrixDescription": "Entrez le nom d'utilisateur et le mot de passe de l'utilisateur pour l’utilisateur comme bot. Une fois soumis, l'application va redémarrer.",
|
"linkMatrixDescription": "Entrez le nom d'utilisateur et le mot de passe de l'utilisateur pour l’utilisateur comme bot. Une fois soumis, l'application va redémarrer.",
|
||||||
"searchDiscordUser": "Commencez à taper le nom d'utilisateur Discord pour trouver l'utilisateur.",
|
"searchDiscordUser": "Commencez à taper le nom d'utilisateur Discord pour trouver l'utilisateur.",
|
||||||
"matrixHomeServer": "Adresse du serveur domestique",
|
"matrixHomeServer": "Adresse du serveur"
|
||||||
"saveAsTemplate": "Sauvegarder comme modèle",
|
|
||||||
"templateEnterName": "Entrez un nom pour sauvegarder ce modèle.",
|
|
||||||
"deleteTemplate": "Supprimer le modèle",
|
|
||||||
"templates": "Modèles"
|
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"changedEmailAddress": "Adresse e-mail modifiée de {n}.",
|
"changedEmailAddress": "Adresse e-mail modifiée de {n}.",
|
||||||
@ -145,8 +141,7 @@
|
|||||||
"noUpdatesAvailable": "Aucune nouvelle mise à jour disponible.",
|
"noUpdatesAvailable": "Aucune nouvelle mise à jour disponible.",
|
||||||
"telegramVerified": "Compte Telegram vérifié.",
|
"telegramVerified": "Compte Telegram vérifié.",
|
||||||
"updateAppliedRefresh": "Mise à jour appliquée, veuillez actualiser.",
|
"updateAppliedRefresh": "Mise à jour appliquée, veuillez actualiser.",
|
||||||
"accountConnected": "Compte connecté.",
|
"accountConnected": "Compte connecté."
|
||||||
"savedAnnouncement": "Annonce enregistrée."
|
|
||||||
},
|
},
|
||||||
"quantityStrings": {
|
"quantityStrings": {
|
||||||
"modifySettingsFor": {
|
"modifySettingsFor": {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"pageTitle": "Créer un compte Jellyfin",
|
"pageTitle": "Créer un compte Jellyfin",
|
||||||
"createAccountHeader": "Création du compte",
|
"createAccountHeader": "Création du compte",
|
||||||
"accountDetails": "Détails",
|
"accountDetails": "Détails",
|
||||||
"emailAddress": "E-mail",
|
"emailAddress": "Email",
|
||||||
"username": "Nom d'utilisateur",
|
"username": "Nom d'utilisateur",
|
||||||
"password": "Mot de passe",
|
"password": "Mot de passe",
|
||||||
"reEnterPassword": "Confirmez mot de passe",
|
"reEnterPassword": "Confirmez mot de passe",
|
||||||
|
@ -15,11 +15,7 @@
|
|||||||
"serverAddress": "Adresse du serveur",
|
"serverAddress": "Adresse du serveur",
|
||||||
"emailSubject": "Objet de l'e-mail",
|
"emailSubject": "Objet de l'e-mail",
|
||||||
"URL": "URL",
|
"URL": "URL",
|
||||||
"apiKey": "Clé API",
|
"apiKey": "Clé API"
|
||||||
"errorUserDisabled": "L'utilisateur est peut-être désactivé.",
|
|
||||||
"error404": "404, vérifiez l'URL interne.",
|
|
||||||
"errorInvalidUserPass": "Nom d'utilisateur/mot de passe invalide.",
|
|
||||||
"errorNotAdmin": "L'utilisateur n'est pas autorisé à gérer le serveur."
|
|
||||||
},
|
},
|
||||||
"startPage": {
|
"startPage": {
|
||||||
"welcome": "Bienvenue !",
|
"welcome": "Bienvenue !",
|
||||||
@ -29,7 +25,7 @@
|
|||||||
},
|
},
|
||||||
"endPage": {
|
"endPage": {
|
||||||
"finished": "Terminé !",
|
"finished": "Terminé !",
|
||||||
"restartMessage": "Vous pouvez configurer les bots Discord/Telegram/Matrix, personnaliser vos messages et plus encore dans Paramètres. Cliquez ci-dessous pour redémarrer, puis actualisez la page.",
|
"restartMessage": "Il y a d'autres paramètres que vous pouvez configurer sur la page administrateur. Cliquez en dessous pour redémarrer, rafraichissez ensuite la page.",
|
||||||
"refreshPage": "Rafraichir"
|
"refreshPage": "Rafraichir"
|
||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
@ -90,16 +86,16 @@
|
|||||||
"mailgunApiURL": "URL de l'API"
|
"mailgunApiURL": "URL de l'API"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"title": "Notifications d'administrateur",
|
"title": "Notifications",
|
||||||
"description": "Si activé, vous pouvez choisir (par invitation) de recevoir un message lorsque l'invitation expire, ou lorsque l'utilisateur est créé. Si vous ne choisissez pas la méthode de connexion par Jellyfin, assurez-vous d'avoir fourni votre adresse e-mail ou un autre moyen de contact."
|
"description": "Si activé, vous pouvez choisir (par invitation) de recevoir un e-mail lorsque l'invitation expire, ou lorsque l'utilisateur est créé. Si vous ne choisissez pas la méthode de connexion par Jellyfin, assurez-vous d'avoir fourni votre adresse e-mail."
|
||||||
},
|
},
|
||||||
"welcomeEmails": {
|
"welcomeEmails": {
|
||||||
"title": "Messages de bienvenue",
|
"title": "E-mails de bienvenue",
|
||||||
"description": "Si activé, un message sera envoyé aux nouveaux utilisateurs avec l'URL de Jellyfin/Emby et leur nom d'utilisateur."
|
"description": "Si activé, un e-mail sera envoyé aux nouveaux utilisateurs avec l'URL de Jellyfin/Emby et leur nom d'utilisateur."
|
||||||
},
|
},
|
||||||
"inviteEmails": {
|
"inviteEmails": {
|
||||||
"title": "Messages d'invitation",
|
"title": "E-mails d'invitation",
|
||||||
"description": "Si activé, vous pouvez envoyer une invitation directement à l'adresse e-mail, le Discord ou Matrix de l'utilisateur. Parce que vous pourriez utiliser un reverse proxy, vous devez renseigner l'URL d'accès aux invitations. Renseignez votre Base URL et ajoutez '/invite'."
|
"description": "Si activé, vous pouvez envoyer une invitation directement à l'adresse e-mail de l'utilisateur. Parce que vous pourriez utiliser un reverse proxy, vous devez renseigner l'URL d'accès aux invitations. Renseignez votre Base URL et ajoutez '/invite'."
|
||||||
},
|
},
|
||||||
"passwordResets": {
|
"passwordResets": {
|
||||||
"title": "Réinitialisation de mot de passe",
|
"title": "Réinitialisation de mot de passe",
|
||||||
@ -108,9 +104,7 @@
|
|||||||
"pathToJellyfinNotice": "Si vous ne savez pas où c'est, essayez de réinitialiser votre mot de passe dans Jellyfin. Une popup avec '<path to jellyfin>/passwordreset-*.json' apparaitra.",
|
"pathToJellyfinNotice": "Si vous ne savez pas où c'est, essayez de réinitialiser votre mot de passe dans Jellyfin. Une popup avec '<path to jellyfin>/passwordreset-*.json' apparaitra.",
|
||||||
"resetLinks": "Envoyer un lien plutôt qu'un PIN",
|
"resetLinks": "Envoyer un lien plutôt qu'un PIN",
|
||||||
"resetLinksNotice": "Si l'intégration est activée, utilisez ceci pour synchroniser les réinitialisations de mots de passe Jellyfin avec Ombi.",
|
"resetLinksNotice": "Si l'intégration est activée, utilisez ceci pour synchroniser les réinitialisations de mots de passe Jellyfin avec Ombi.",
|
||||||
"resetLinksLanguage": "Langue du lien de réinitialisation par défaut",
|
"resetLinksLanguage": "Langue du lien de réinitialisation par défaut"
|
||||||
"setPassword": "Définir le mot de passe via le lien",
|
|
||||||
"setPasswordNotice": "L'activation de cette option signifie que l'utilisateur n'a pas à modifier son mot de passe à partir du code PIN après la réinitialisation. La validation du mot de passe sera également appliquée."
|
|
||||||
},
|
},
|
||||||
"passwordValidation": {
|
"passwordValidation": {
|
||||||
"title": "Validation du mot de passe",
|
"title": "Validation du mot de passe",
|
||||||
@ -139,9 +133,5 @@
|
|||||||
"updateChannel": "Mettre à jour la chaîne",
|
"updateChannel": "Mettre à jour la chaîne",
|
||||||
"stable": "Stable",
|
"stable": "Stable",
|
||||||
"unstable": "Instable"
|
"unstable": "Instable"
|
||||||
},
|
|
||||||
"messages": {
|
|
||||||
"title": "Messages",
|
|
||||||
"description": "jfa-go peut envoyer des réinitialisations de mot de passe et divers messages par e-mail, Discord, Telegram et/ou Matrix. Vous pouvez configurer l'e-mail ci-dessous, et les autres peuvent être configurés dans les paramètres plus tard. Les instructions se trouvent sur le {n}. Si vous n'en avez pas besoin, vous pouvez désactiver ces fonctionnalités ici."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user