diff --git a/email/http.go b/email/http.go new file mode 100644 index 00000000..e0d3383d --- /dev/null +++ b/email/http.go @@ -0,0 +1,75 @@ +// Copyright 2023 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package email + +import ( + "bytes" + "fmt" + "net/http" + + "github.com/casdoor/casdoor/proxy" +) + +type HttpEmailProvider struct { + endpoint string + method string +} + +func NewHttpEmailProvider(endpoint string, method string) *HttpEmailProvider { + client := &HttpEmailProvider{ + endpoint: endpoint, + method: method, + } + return client +} + +func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error { + req, err := http.NewRequest(c.method, c.endpoint, bytes.NewBufferString(content)) + if err != nil { + return err + } + + if c.method == "POST" { + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.PostForm = map[string][]string{ + "fromName": {fromName}, + "toAddress": {toAddress}, + "subject": {subject}, + "content": {content}, + } + } else if c.method == "GET" { + q := req.URL.Query() + q.Add("fromName", fromName) + q.Add("toAddress", toAddress) + q.Add("subject", subject) + q.Add("content", content) + req.URL.RawQuery = q.Encode() + } else { + return fmt.Errorf("HttpEmailProvider's Send() error, unsupported method: %s", c.method) + } + + httpClient := proxy.DefaultHttpClient + resp, err := httpClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("HttpEmailProvider's Send() error, custom HTTP Email request failed with status: %s", resp.Status) + } + + return err +} diff --git a/email/provider.go b/email/provider.go index 71949968..be708716 100644 --- a/email/provider.go +++ b/email/provider.go @@ -18,9 +18,11 @@ type EmailProvider interface { Send(fromAddress string, fromName, toAddress string, subject string, content string) error } -func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, disableSsl bool) EmailProvider { +func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, disableSsl bool, endpoint string, method string) EmailProvider { if typ == "Azure ACS" { return NewAzureACSEmailProvider(clientSecret, host) + } else if typ == "Custom HTTP Email" { + return NewHttpEmailProvider(endpoint, method) } else { return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, disableSsl) } diff --git a/object/email.go b/object/email.go index 0b217bfe..7c0ca411 100644 --- a/object/email.go +++ b/object/email.go @@ -36,7 +36,7 @@ func getDialer(provider *Provider) *gomail.Dialer { } func SendEmail(provider *Provider, title string, content string, dest string, sender string) error { - emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.DisableSsl) + emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.DisableSsl, provider.Endpoint, provider.Method) fromAddress := provider.ClientId2 if fromAddress == "" { diff --git a/web/src/ProviderEditPage.js b/web/src/ProviderEditPage.js index 3508cbb7..1d3fdcf4 100644 --- a/web/src/ProviderEditPage.js +++ b/web/src/ProviderEditPage.js @@ -528,9 +528,12 @@ class ProviderEditPage extends React.Component { this.updateProviderField("customTokenUrl", "https://door.casdoor.com/api/login/oauth/access_token"); this.updateProviderField("customUserInfoUrl", "https://door.casdoor.com/api/userinfo"); } else if (value === "Custom HTTP SMS") { - this.updateProviderField("endpoint", "https://example.com/send-custom-http"); + this.updateProviderField("endpoint", "https://example.com/send-custom-http-sms"); this.updateProviderField("method", "GET"); this.updateProviderField("title", "code"); + } else if (value === "Custom HTTP Email") { + this.updateProviderField("endpoint", "https://example.com/send-custom-http-email"); + this.updateProviderField("method", "POST"); } else if (value === "Custom HTTP") { this.updateProviderField("method", "GET"); this.updateProviderField("title", ""); @@ -768,7 +771,7 @@ class ProviderEditPage extends React.Component { ) } - {this.state.provider.category === "Storage" || this.state.provider.type === "Custom HTTP SMS" ? ( + {this.state.provider.category === "Storage" || ["Custom HTTP SMS", "Custom HTTP Email"].includes(this.state.provider.type) ? (
{["Local File System"].includes(this.state.provider.type) ? null : ( @@ -1017,7 +1020,7 @@ class ProviderEditPage extends React.Component { ) } { - !["Custom HTTP SMS"].includes(this.state.provider.type) ? null : ( + !["Custom HTTP SMS", "Custom HTTP Email"].includes(this.state.provider.type) ? null : ( diff --git a/web/src/Setting.js b/web/src/Setting.js index dfd1607f..ab495cd2 100644 --- a/web/src/Setting.js +++ b/web/src/Setting.js @@ -169,6 +169,10 @@ export const OtherProviderInfo = { logo: `${StaticBaseUrl}/img/social_azure.png`, url: "https://learn.microsoft.com/zh-cn/azure/communication-services", }, + "Custom HTTP Email": { + logo: `${StaticBaseUrl}/img/social_default.png`, + url: "https://casdoor.org/docs/provider/email/overview", + }, }, Storage: { "Local File System": { @@ -985,6 +989,7 @@ export function getProviderTypeOptions(category) { {id: "SUBMAIL", name: "SUBMAIL"}, {id: "Mailtrap", name: "Mailtrap"}, {id: "Azure ACS", name: "Azure ACS"}, + {id: "Custom HTTP Email", name: "Custom HTTP Email"}, ] ); } else if (category === "SMS") {