Skip to content

The use of Nodemailer is outdated. #4194

@kazto

Description

@kazto

Is your feature request related to a problem? Please describe.

PDS Version: 0.4.169

Boot up PDS by docker (systemctl start pds.service) and docker logs are displayed as follows:

(node:7) [DEP0170] DeprecationWarning: The URL smtps://SMTPUSERNAME:[email protected] is invalid. Future versions of Node.js will throw an error.
(Use `node --trace-deprecation ...` to show where the warning was created)
{"level":30,"time":1757406161459,"pid":7,"hostname":"MYHOSTNAME","name":"pds","msg":"pds has started"}

my /pds/pds.env

PDS_EMAIL_SMTP_URL=smtps://SMTPUSERNAME:[email protected]
[email protected]

then, do "Email Confirmation" from the bsky.app and show logs.

{
  "level": 50,
  "time": 1757406305802,
  "pid": 7,
  "hostname": "MYHOSTNAME",
  "name": "xrpc-server",
  "req": {
    "id": 17,
    "method": "POST",
    "url": "/xrpc/com.atproto.server.requestEmailConfirmation",
    "query": {},
    "params": {},
    "headers": {
      "host": "MYHOSTNAME",
      "user-agent": "okhttp/4.12.0",
      "content-length": "0",
      "accept-encoding": "gzip",
      "atproto-accept-labelers": "did:plc:ar7c4by46qjdydhdevvrndac;redact, did:plc:vhgppeyjwgrr37vm4v6ggd5a;redact",
      "atproto-proxy": "did:web:api.bsky.app#bsky_appview",
      "authorization": "Bearer did:plc:4jjro6pwpnqienb74usea55d",
      "via": "2.0 Caddy",
      "x-forwarded-for": "133.32.217.188",
      "x-forwarded-host": "MYHOSTNAME",
      "x-forwarded-proto": "https"
    }
  },
  "err": {
    "code": "EDNS",
    "message": "getaddrinfo ENOTFOUND *SMTPUSERNAME*" # <= Not MAILSERVER.EXAMPLE.COM !!
  },
  "nsid": "com.atproto.server.requestEmailConfirmation",
  "type": 500,
  "status": 500,
  "payload": {
    "error": "InternalServerError",
    "message": "Internal Server Error"
  },
  "msg": "unhandled exception in xrpc method com.atproto.server.requestEmailConfirmation"
}

Describe the solution you'd like

I think the use of Nodemailer is outdated.

Describe alternatives you've considered

Current Nodemailer example :

const transporter = nodemailer.createTransport({
  pool: true,
  host: "smtp.example.com",
  port: 465,
  secure: true, // use TLS
  auth: {
    user: "username",
    pass: "password",
  },
});

but current atproto:

packages/pds/src/config/config.ts L141:

  let emailCfg: ServerConfig['email']
  if (!env.emailFromAddress && !env.emailSmtpUrl) {
    emailCfg = null
  } else {
    if (!env.emailFromAddress || !env.emailSmtpUrl) {
      throw new Error(
        'Partial email config, must set both emailFromAddress and emailSmtpUrl',
      )
    }
    emailCfg = {
      smtpUrl: env.emailSmtpUrl,
      fromAddress: env.emailFromAddress,
    }
  }

packages/pds/src/config/config.ts L483:

export type EmailConfig = {
  smtpUrl: string
  fromAddress: string
}

packages/pds/src/context.ts:

    const mailTransport =
      cfg.email !== null
        ? nodemailer.createTransport(cfg.email.smtpUrl)
        : nodemailer.createTransport({ jsonTransport: true })

My oppinion:

  1. Increase the entries in pds.env.
# backward compatibility
PDS_EMAIL_SMTP_URL=smtps://SMTPUSERNAME:[email protected]
# or new entries
PDS_EMAIL_SMTP_HOST=smtp.example.com
PDS_EMAIL_SMTP_POOLING=true
PDS_EMAIL_SMTP_PORT=465 # or 587 ...
PDS_EMAIL_SMTP_USE_TLS=true
PDS_EMAIL_SMTP_AUTH_USER=username
PDS_EMAIL_SMTP_AUTH_PASS=password

[email protected]
  1. add type

packages/pds/src/config/config.ts L483:

export type EmailConfig = {
  smtpUrl: string
  fromAddress: string
} | {
  smtpHost: string
  smtpPort: number
  smtpPool?: boolean
  smtpSecure?: boolean
  smtpAuthUser?: string
  smtpAuthPassword? string
  fromAddress: string
}
  1. Set env value to object.

packages/pds/src/config/config.ts L141:

  let emailCfg: ServerConfig['email']
  if (!env.emailFromAddress && !env.emailSmtpUrl) {
    emailCfg = null
  } else {
    if (!env.emailFromAddress || !env.emailSmtpUrl || !env.emailSmtpHost) {
      throw new Error(
        'Partial email config, must set both emailFromAddress and emailSmtpUrl',
      )
    }
    emailCfg = env.emailSmtpUrl ? {
      smtpUrl: env.emailSmtpUrl,
      fromAddress: env.emailFromAddress,
    } : {
      smtpHost: env.emailSmtpHost,
      smtpPort: env.emailSmtpPort ?? 587,
      fromAddress: env.emailFromAddress,
    }

    // there is room for ingenuity.
    if (env.emailSmtpPool) emailCfg = { ...emailCfg, smtpPool: true }
    if (env.emailSmtpSecure) emailCfg = { ...emailCfg, smtpSecure: true }
    if (env.emailSmtpAuthUser && env.emailSmtpAuthPass) {
      emailCfg = {
        ...emailCfg,
        auth: {
          user: env.emailSmtpAuthUser,
          pass: env.emailSmtpAuthPass
        }
      }
    }
  }

Additional context

none.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions