mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-03-29 18:56:36 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12d60fea23 | ||
|
|
2d733fc79f | ||
|
|
a421d01e0c | ||
|
|
1026ee4f5b | ||
|
|
cddfe8fa4c | ||
|
|
ef25f6b6b8 | ||
|
|
1652cc65f3 | ||
|
|
4bafee4f58 |
23
CHANGELOG.md
23
CHANGELOG.md
@@ -1,3 +1,26 @@
|
|||||||
|
## [](https://github.com/pocket-id/pocket-id/compare/v0.35.5...v) (2025-03-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* support `LOGIN` authentication method for SMTP ([#292](https://github.com/pocket-id/pocket-id/issues/292)) ([2d733fc](https://github.com/pocket-id/pocket-id/commit/2d733fc79faefca23d54b22768029c3ba3427410))
|
||||||
|
|
||||||
|
## [](https://github.com/pocket-id/pocket-id/compare/v0.35.4...v) (2025-03-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* profile picture orientation if image is rotated with EXIF ([1026ee4](https://github.com/pocket-id/pocket-id/commit/1026ee4f5b5c7fda78b65c94a5d0f899525defd1))
|
||||||
|
|
||||||
|
## [](https://github.com/pocket-id/pocket-id/compare/v0.35.3...v) (2025-03-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add `groups` scope and claim to well known endpoint ([4bafee4](https://github.com/pocket-id/pocket-id/commit/4bafee4f58f5a76898cf66d6192916d405eea389))
|
||||||
|
* profile picture of other user can't be updated ([#273](https://github.com/pocket-id/pocket-id/issues/273)) ([ef25f6b](https://github.com/pocket-id/pocket-id/commit/ef25f6b6b84b52f1310d366d40aa3769a6fe9bef))
|
||||||
|
* support POST for OIDC userinfo endpoint ([1652cc6](https://github.com/pocket-id/pocket-id/commit/1652cc65f3f966d018d81a1ae22abb5ff1b4c47b))
|
||||||
|
|
||||||
## [](https://github.com/pocket-id/pocket-id/compare/v0.35.2...v) (2025-02-25)
|
## [](https://github.com/pocket-id/pocket-id/compare/v0.35.2...v) (2025-02-25)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ go 1.23.1
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/caarlos0/env/v11 v11.3.1
|
github.com/caarlos0/env/v11 v11.3.1
|
||||||
|
github.com/disintegration/imageorient v0.0.0-20180920195336-8147d86e83ec
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
|
||||||
|
github.com/emersion/go-smtp v0.21.3
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0
|
github.com/fxamacker/cbor/v2 v2.7.0
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/go-co-op/gocron/v2 v2.15.0
|
github.com/go-co-op/gocron/v2 v2.15.0
|
||||||
@@ -30,6 +33,7 @@ require (
|
|||||||
github.com/bytedance/sonic v1.12.8 // indirect
|
github.com/bytedance/sonic v1.12.8 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.3 // indirect
|
github.com/bytedance/sonic/loader v0.2.3 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||||
|
github.com/disintegration/gift v1.1.2 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
github.com/gin-contrib/sse v1.0.0 // indirect
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dhui/dktest v0.4.4 h1:+I4s6JRE1yGuqflzwqG+aIaMdgXIorCf5P98JnaAWa8=
|
github.com/dhui/dktest v0.4.4 h1:+I4s6JRE1yGuqflzwqG+aIaMdgXIorCf5P98JnaAWa8=
|
||||||
github.com/dhui/dktest v0.4.4/go.mod h1:4+22R4lgsdAXrDyaH4Nqx2JEz2hLp49MqQmm9HLCQhM=
|
github.com/dhui/dktest v0.4.4/go.mod h1:4+22R4lgsdAXrDyaH4Nqx2JEz2hLp49MqQmm9HLCQhM=
|
||||||
|
github.com/disintegration/gift v1.1.2 h1:9ZyHJr+kPamiH10FX3Pynt1AxFUob812bU9Wt4GMzhs=
|
||||||
|
github.com/disintegration/gift v1.1.2/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
|
||||||
|
github.com/disintegration/imageorient v0.0.0-20180920195336-8147d86e83ec h1:YrB6aVr9touOt75I9O1SiancmR2GMg45U9UYf0gtgWg=
|
||||||
|
github.com/disintegration/imageorient v0.0.0-20180920195336-8147d86e83ec/go.mod h1:K0KBFIr1gWu/C1Gp10nFAcAE4hsB7JxE6OgLijrJ8Sk=
|
||||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
@@ -32,6 +36,10 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj
|
|||||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
|
||||||
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
|
github.com/emersion/go-smtp v0.21.3 h1:7uVwagE8iPYE48WhNsng3RRpCUpFvNl39JGNSIyGVMY=
|
||||||
|
github.com/emersion/go-smtp v0.21.3/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||||
|
|||||||
@@ -94,6 +94,11 @@ type NotSignedInError struct{}
|
|||||||
func (e *NotSignedInError) Error() string { return "You are not signed in" }
|
func (e *NotSignedInError) Error() string { return "You are not signed in" }
|
||||||
func (e *NotSignedInError) HttpStatusCode() int { return http.StatusUnauthorized }
|
func (e *NotSignedInError) HttpStatusCode() int { return http.StatusUnauthorized }
|
||||||
|
|
||||||
|
type MissingAccessToken struct{}
|
||||||
|
|
||||||
|
func (e *MissingAccessToken) Error() string { return "Missing access token" }
|
||||||
|
func (e *MissingAccessToken) HttpStatusCode() int { return http.StatusUnauthorized }
|
||||||
|
|
||||||
type MissingPermissionError struct{}
|
type MissingPermissionError struct{}
|
||||||
|
|
||||||
func (e *MissingPermissionError) Error() string {
|
func (e *MissingPermissionError) Error() string {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ func NewOidcController(group *gin.RouterGroup, jwtAuthMiddleware *middleware.Jwt
|
|||||||
|
|
||||||
group.POST("/oidc/token", oc.createTokensHandler)
|
group.POST("/oidc/token", oc.createTokensHandler)
|
||||||
group.GET("/oidc/userinfo", oc.userInfoHandler)
|
group.GET("/oidc/userinfo", oc.userInfoHandler)
|
||||||
|
group.POST("/oidc/userinfo", oc.userInfoHandler)
|
||||||
group.POST("/oidc/end-session", oc.EndSessionHandler)
|
group.POST("/oidc/end-session", oc.EndSessionHandler)
|
||||||
group.GET("/oidc/end-session", oc.EndSessionHandler)
|
group.GET("/oidc/end-session", oc.EndSessionHandler)
|
||||||
|
|
||||||
@@ -111,7 +112,14 @@ func (oc *OidcController) createTokensHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (oc *OidcController) userInfoHandler(c *gin.Context) {
|
func (oc *OidcController) userInfoHandler(c *gin.Context) {
|
||||||
token := strings.Split(c.GetHeader("Authorization"), " ")[1]
|
authHeaderSplit := strings.Split(c.GetHeader("Authorization"), " ")
|
||||||
|
if len(authHeaderSplit) != 2 {
|
||||||
|
c.Error(&common.MissingAccessToken{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token := authHeaderSplit[1]
|
||||||
|
|
||||||
jwtClaims, err := oc.jwtService.VerifyOauthAccessToken(token)
|
jwtClaims, err := oc.jwtService.VerifyOauthAccessToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(err)
|
c.Error(err)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func NewUserController(group *gin.RouterGroup, jwtAuthMiddleware *middleware.Jwt
|
|||||||
group.GET("/users/:id/profile-picture.png", uc.getUserProfilePictureHandler)
|
group.GET("/users/:id/profile-picture.png", uc.getUserProfilePictureHandler)
|
||||||
group.GET("/users/me/profile-picture.png", jwtAuthMiddleware.Add(false), uc.getCurrentUserProfilePictureHandler)
|
group.GET("/users/me/profile-picture.png", jwtAuthMiddleware.Add(false), uc.getCurrentUserProfilePictureHandler)
|
||||||
group.PUT("/users/:id/profile-picture", jwtAuthMiddleware.Add(true), uc.updateUserProfilePictureHandler)
|
group.PUT("/users/:id/profile-picture", jwtAuthMiddleware.Add(true), uc.updateUserProfilePictureHandler)
|
||||||
group.PUT("/users/me/profile-picture", jwtAuthMiddleware.Add(false), uc.updateUserProfilePictureHandler)
|
group.PUT("/users/me/profile-picture", jwtAuthMiddleware.Add(false), uc.updateCurrentUserProfilePictureHandler)
|
||||||
|
|
||||||
group.POST("/users/:id/one-time-access-token", jwtAuthMiddleware.Add(true), uc.createOneTimeAccessTokenHandler)
|
group.POST("/users/:id/one-time-access-token", jwtAuthMiddleware.Add(true), uc.createOneTimeAccessTokenHandler)
|
||||||
group.POST("/one-time-access-token/:token", rateLimitMiddleware.Add(rate.Every(10*time.Second), 5), uc.exchangeOneTimeAccessTokenHandler)
|
group.POST("/one-time-access-token/:token", rateLimitMiddleware.Add(rate.Every(10*time.Second), 5), uc.exchangeOneTimeAccessTokenHandler)
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ func (wkc *WellKnownController) openIDConfigurationHandler(c *gin.Context) {
|
|||||||
"userinfo_endpoint": appUrl + "/api/oidc/userinfo",
|
"userinfo_endpoint": appUrl + "/api/oidc/userinfo",
|
||||||
"end_session_endpoint": appUrl + "/api/oidc/end-session",
|
"end_session_endpoint": appUrl + "/api/oidc/end-session",
|
||||||
"jwks_uri": appUrl + "/.well-known/jwks.json",
|
"jwks_uri": appUrl + "/.well-known/jwks.json",
|
||||||
"scopes_supported": []string{"openid", "profile", "email"},
|
"scopes_supported": []string{"openid", "profile", "email", "groups"},
|
||||||
"claims_supported": []string{"sub", "given_name", "family_name", "name", "email", "email_verified", "preferred_username", "picture"},
|
"claims_supported": []string{"sub", "given_name", "family_name", "name", "email", "email_verified", "preferred_username", "picture", "groups"},
|
||||||
"response_types_supported": []string{"code", "id_token"},
|
"response_types_supported": []string{"code", "id_token"},
|
||||||
"subject_types_supported": []string{"public"},
|
"subject_types_supported": []string{"public"},
|
||||||
"id_token_signing_alg_values_supported": []string{"RS256"},
|
"id_token_signing_alg_values_supported": []string{"RS256"},
|
||||||
|
|||||||
@@ -3,27 +3,23 @@ package service
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
htemplate "html/template"
|
"github.com/emersion/go-sasl"
|
||||||
"mime/multipart"
|
"github.com/emersion/go-smtp"
|
||||||
"mime/quotedprintable"
|
|
||||||
"net"
|
|
||||||
"net/smtp"
|
|
||||||
"net/textproto"
|
|
||||||
"os"
|
|
||||||
ttemplate "text/template"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pocket-id/pocket-id/backend/internal/common"
|
"github.com/pocket-id/pocket-id/backend/internal/common"
|
||||||
"github.com/pocket-id/pocket-id/backend/internal/model"
|
"github.com/pocket-id/pocket-id/backend/internal/model"
|
||||||
"github.com/pocket-id/pocket-id/backend/internal/utils/email"
|
"github.com/pocket-id/pocket-id/backend/internal/utils/email"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
htemplate "html/template"
|
||||||
|
"mime/multipart"
|
||||||
|
"mime/quotedprintable"
|
||||||
|
"net/textproto"
|
||||||
|
"os"
|
||||||
|
ttemplate "text/template"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var netDialer = &net.Dialer{
|
|
||||||
Timeout: 3 * time.Second,
|
|
||||||
}
|
|
||||||
|
|
||||||
type EmailService struct {
|
type EmailService struct {
|
||||||
appConfigService *AppConfigService
|
appConfigService *AppConfigService
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
@@ -114,18 +110,14 @@ func (srv *EmailService) getSmtpClient() (client *smtp.Client, err error) {
|
|||||||
ServerName: srv.appConfigService.DbConfig.SmtpHost.Value,
|
ServerName: srv.appConfigService.DbConfig.SmtpHost.Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to the SMTP server
|
|
||||||
// Connect to the SMTP server based on TLS setting
|
// Connect to the SMTP server based on TLS setting
|
||||||
switch srv.appConfigService.DbConfig.SmtpTls.Value {
|
switch srv.appConfigService.DbConfig.SmtpTls.Value {
|
||||||
case "none":
|
case "none":
|
||||||
client, err = srv.connectToSmtpServer(smtpAddress)
|
client, err = smtp.Dial(smtpAddress)
|
||||||
case "tls":
|
case "tls":
|
||||||
client, err = srv.connectToSmtpServerUsingImplicitTLS(
|
client, err = smtp.DialTLS(smtpAddress, tlsConfig)
|
||||||
smtpAddress,
|
|
||||||
tlsConfig,
|
|
||||||
)
|
|
||||||
case "starttls":
|
case "starttls":
|
||||||
client, err = srv.connectToSmtpServerUsingStartTLS(
|
client, err = smtp.DialStartTLS(
|
||||||
smtpAddress,
|
smtpAddress,
|
||||||
tlsConfig,
|
tlsConfig,
|
||||||
)
|
)
|
||||||
@@ -136,87 +128,39 @@ func (srv *EmailService) getSmtpClient() (client *smtp.Client, err error) {
|
|||||||
return nil, fmt.Errorf("failed to connect to SMTP server: %w", err)
|
return nil, fmt.Errorf("failed to connect to SMTP server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.CommandTimeout = 10 * time.Second
|
||||||
|
|
||||||
|
// Send the HELO command
|
||||||
|
if err := srv.sendHelloCommand(client); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to send HELO command: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set up the authentication if user or password are set
|
// Set up the authentication if user or password are set
|
||||||
smtpUser := srv.appConfigService.DbConfig.SmtpUser.Value
|
smtpUser := srv.appConfigService.DbConfig.SmtpUser.Value
|
||||||
smtpPassword := srv.appConfigService.DbConfig.SmtpPassword.Value
|
smtpPassword := srv.appConfigService.DbConfig.SmtpPassword.Value
|
||||||
|
|
||||||
if smtpUser != "" || smtpPassword != "" {
|
if smtpUser != "" || smtpPassword != "" {
|
||||||
auth := smtp.PlainAuth("",
|
// Authenticate with plain auth
|
||||||
srv.appConfigService.DbConfig.SmtpUser.Value,
|
auth := sasl.NewPlainClient("", smtpUser, smtpPassword)
|
||||||
srv.appConfigService.DbConfig.SmtpPassword.Value,
|
|
||||||
srv.appConfigService.DbConfig.SmtpHost.Value,
|
|
||||||
)
|
|
||||||
if err := client.Auth(auth); err != nil {
|
if err := client.Auth(auth); err != nil {
|
||||||
return nil, fmt.Errorf("failed to authenticate SMTP client: %w", err)
|
// If the server does not support plain auth, try login auth
|
||||||
|
var smtpErr *smtp.SMTPError
|
||||||
|
ok := errors.As(err, &smtpErr)
|
||||||
|
if ok && smtpErr.Code == smtp.ErrAuthUnknownMechanism.Code {
|
||||||
|
auth = sasl.NewLoginClient(smtpUser, smtpPassword)
|
||||||
|
err = client.Auth(auth)
|
||||||
|
}
|
||||||
|
// Both plain and login auth failed
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to authenticate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return client, err
|
return client, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *EmailService) connectToSmtpServer(serverAddr string) (*smtp.Client, error) {
|
|
||||||
conn, err := netDialer.Dial("tcp", serverAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to connect to SMTP server: %w", err)
|
|
||||||
}
|
|
||||||
client, err := smtp.NewClient(conn, srv.appConfigService.DbConfig.SmtpHost.Value)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, fmt.Errorf("failed to create SMTP client: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := srv.sendHelloCommand(client); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to say hello to SMTP server: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return client, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *EmailService) connectToSmtpServerUsingImplicitTLS(serverAddr string, tlsConfig *tls.Config) (*smtp.Client, error) {
|
|
||||||
tlsDialer := &tls.Dialer{
|
|
||||||
NetDialer: netDialer,
|
|
||||||
Config: tlsConfig,
|
|
||||||
}
|
|
||||||
conn, err := tlsDialer.Dial("tcp", serverAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to connect to SMTP server: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := smtp.NewClient(conn, srv.appConfigService.DbConfig.SmtpHost.Value)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, fmt.Errorf("failed to create SMTP client: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := srv.sendHelloCommand(client); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to say hello to SMTP server: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *EmailService) connectToSmtpServerUsingStartTLS(serverAddr string, tlsConfig *tls.Config) (*smtp.Client, error) {
|
|
||||||
conn, err := netDialer.Dial("tcp", serverAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to connect to SMTP server: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := smtp.NewClient(conn, srv.appConfigService.DbConfig.SmtpHost.Value)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, fmt.Errorf("failed to create SMTP client: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := srv.sendHelloCommand(client); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to say hello to SMTP server: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := client.StartTLS(tlsConfig); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to start TLS: %w", err)
|
|
||||||
}
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *EmailService) sendHelloCommand(client *smtp.Client) error {
|
func (srv *EmailService) sendHelloCommand(client *smtp.Client) error {
|
||||||
hostname, err := os.Hostname()
|
hostname, err := os.Hostname()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -228,23 +172,33 @@ func (srv *EmailService) sendHelloCommand(client *smtp.Client) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (srv *EmailService) sendEmailContent(client *smtp.Client, toEmail email.Address, c *email.Composer) error {
|
func (srv *EmailService) sendEmailContent(client *smtp.Client, toEmail email.Address, c *email.Composer) error {
|
||||||
if err := client.Mail(srv.appConfigService.DbConfig.SmtpFrom.Value); err != nil {
|
// Set the sender
|
||||||
|
if err := client.Mail(srv.appConfigService.DbConfig.SmtpFrom.Value, nil); err != nil {
|
||||||
return fmt.Errorf("failed to set sender: %w", err)
|
return fmt.Errorf("failed to set sender: %w", err)
|
||||||
}
|
}
|
||||||
if err := client.Rcpt(toEmail.Email); err != nil {
|
|
||||||
|
// Set the recipient
|
||||||
|
if err := client.Rcpt(toEmail.Email, nil); err != nil {
|
||||||
return fmt.Errorf("failed to set recipient: %w", err)
|
return fmt.Errorf("failed to set recipient: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get a writer to write the email data
|
||||||
w, err := client.Data()
|
w, err := client.Data()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to start data: %w", err)
|
return fmt.Errorf("failed to start data: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write the email content
|
||||||
_, err = w.Write([]byte(c.String()))
|
_, err = w.Write([]byte(c.String()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to write email data: %w", err)
|
return fmt.Errorf("failed to write email data: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close the writer
|
||||||
if err := w.Close(); err != nil {
|
if err := w.Close(); err != nil {
|
||||||
return fmt.Errorf("failed to close data writer: %w", err)
|
return fmt.Errorf("failed to close data writer: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package profilepicture
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/disintegration/imageorient"
|
||||||
"github.com/disintegration/imaging"
|
"github.com/disintegration/imaging"
|
||||||
"github.com/pocket-id/pocket-id/backend/resources"
|
"github.com/pocket-id/pocket-id/backend/resources"
|
||||||
"golang.org/x/image/font"
|
"golang.org/x/image/font"
|
||||||
@@ -18,7 +19,7 @@ const profilePictureSize = 300
|
|||||||
|
|
||||||
// CreateProfilePicture resizes the profile picture to a square
|
// CreateProfilePicture resizes the profile picture to a square
|
||||||
func CreateProfilePicture(file io.Reader) (*bytes.Buffer, error) {
|
func CreateProfilePicture(file io.Reader) (*bytes.Buffer, error) {
|
||||||
img, err := imaging.Decode(file)
|
img, _, err := imageorient.Decode(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to decode image: %w", err)
|
return nil, fmt.Errorf("failed to decode image: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pocket-id-frontend",
|
"name": "pocket-id-frontend",
|
||||||
"version": "0.35.3",
|
"version": "0.35.6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Reference in New Issue
Block a user