Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: postalsys/emailengine
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.69.0
Choose a base ref
...
head repository: postalsys/emailengine
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2.70.0
Choose a head ref
  • 15 commits
  • 80 files changed
  • 2 contributors

Commits on Jun 10, 2026

  1. fix: do not crash at startup when a feature flag env variable is set …

    …to a falsy value
    
    EENGINE_FEATURE_* variables with values like "false" or "0" called the
    nonexistent Set.remove() and threw a TypeError while loading the module.
    Use Set.delete() and anchor the truthy value pattern so that values like
    "latest" no longer enable a flag by accident.
    andris9 committed Jun 10, 2026
    Configuration menu
    Copy the full SHA
    98ad979 View commit details
    Browse the repository at this point in the history
  2. fix: report correct nextAttempt time for queued messages

    The outbox API doubled the actual BullMQ retry delay by using
    2^attemptsMade instead of 2^(attemptsMade-1) for jobs whose attempt
    counter already includes the failed attempt. The submit worker had the
    correct multiplier but advertised a retry time on the final attempt and
    serialized exhausted retries as the 1970 epoch. Also removed a dead
    exhaustion re-check in handleSubmitError that referenced a nonexistent
    jobData.opts field and prevented the feedbackKey failure update.
    andris9 committed Jun 10, 2026
    Configuration menu
    Copy the full SHA
    d5ebdfb View commit details
    Browse the repository at this point in the history
  3. fix: assign unassigned accounts after license activation

    Accounts added while no license was active stayed in the initializing
    state forever because account assignment was only triggered by the
    worker crash recovery path, not when suspended IMAP workers were
    respawned after a license became active.
    andris9 committed Jun 10, 2026
    Configuration menu
    Copy the full SHA
    5677977 View commit details
    Browse the repository at this point in the history
  4. ci: make Docker :latest track releases and harden build scripts [skip…

    … ci]
    
    - release.yaml: also tag releases as :latest on GHCR, matching Docker Hub
    - deploy.yml: publish per-push dev images as :edge instead of :latest, so
      :latest is owned solely by the release workflow
    - deploy.yml: pass EE_COMMIT_HASH=$GITHUB_SHA to update-info.sh
    - getswagger.sh: refuse to start if the port is already in use, bound curl
      requests with --max-time, wait for the server to exit during cleanup
    - update-info.sh: only trust git rev-parse when the repository toplevel is
      the working directory, so a source export nested inside an unrelated git
      repository does not record the parent repository's commit hash
    andris9 committed Jun 10, 2026
    Configuration menu
    Copy the full SHA
    44fd5e1 View commit details
    Browse the repository at this point in the history
  5. fix: align API response schemas with actual responses and fix uncover…

    …ed API bugs
    
    Audit of the Joi response schemas across all API route modules so the
    generated OpenAPI spec matches what the handlers actually return, plus
    fixes for bugs the audit uncovered:
    
    - return 404 (MessageNotFound/FolderNotFound) instead of a false response
      body when a message or folder is not found (Account#assertFound)
    - resolve special-use folder aliases (\Sent, \Trash, ...) for IMAP bulk
      update, move, and delete operations
    - Gmail API: support flags.set, reject unsupported labels.set with 400
      before any API calls, return 404 for unknown folder paths in bulk updates
    - flags.set now takes precedence over flags.add/delete on Gmail and
      Outlook accounts, matching the IMAP backend behavior
    - Outlook: only force-delete messages in bulk delete when the source
      folder is the Trash folder; report a missing Trash folder as 404
    - oauth-token endpoint returns the provider name and the OAuth2 app ID as
      separate fields instead of the app ID in the provider field
    - encrypt mailRuClientSecret, imapProxyServerPassword, and serviceKey
      settings at rest and mask them in GET /v1/settings responses
    - OAuth2 app updates only map the deprecated tenant field into authority
      when explicitly selected via authority='tenant'
    - accounts can be filtered by state 'paused' (API and admin UI)
    - enforce the documented force=true query parameter on the template flush
      endpoint (DELETE /v1/templates/account/{account} without it now fails
      with 400 ForceRequired)
    - assorted fixes: trackOpens flag, custom submit headers, outbox created
      timestamps, token listing IDs, delivery-test SPF comment cleanup, search
      size filters, OAuth2 app listing pagination totals, gateway delete
      response, queue stats resilience, chat success flag semantics
    
    Known gaps left for follow-up: IMAP move target paths do not resolve
    special-use aliases, Gmail bulk label updates do not map special-use
    names, and the OAuth2 create route still accepts the inert tenant field.
    andris9 committed Jun 10, 2026
    Configuration menu
    Copy the full SHA
    abacbf6 View commit details
    Browse the repository at this point in the history
  6. chore: replace jsxgettext with a modern gettext string extractor [ski…

    …p ci]
    
    jsxgettext bundled acorn 5 and crashed with a SyntaxError on any file using
    post-ES2018 syntax (optional chaining etc.), which forced a hardcoded list of
    scanned files and a ban on modern syntax in them.
    
    The new gettext-extract.js parses with acorn 8 (ecmaVersion latest), walks the
    AST for gettext()/ngettext() calls, and merges the strings into
    translations/messages.pot via gettext-parser. Files are discovered dynamically
    under bin/, lib/, and workers/, so new modules with translatable strings are
    picked up automatically - no list to maintain when routes are refactored.
    
    The extracted msgid set is identical to the previous output, the views half of
    the pipeline (xgettext-template) is unchanged, and the "no newer syntax"
    restriction comments were removed from the previously scanned files since the
    constraint no longer applies.
    andris9 committed Jun 10, 2026
    Configuration menu
    Copy the full SHA
    4c30b69 View commit details
    Browse the repository at this point in the history
  7. fix: align remaining API response schemas with actual responses

    Continues the response schema audit: adds response schemas with
    failAction 'log' to the remaining API routes (blocklist, chat,
    delivery-test, export, gateway, license, mailbox, message, oauth2-app,
    outbox, pubsub, settings, stats, submit, template, token, webhook-route),
    extends lib/schemas.js with the shared error response helper usage, and
    simplifies the gettext coverage test.
    andris9 committed Jun 10, 2026
    Configuration menu
    Copy the full SHA
    06136ab View commit details
    Browse the repository at this point in the history

Commits on Jun 11, 2026

  1. chore: quote eslint globs so the whole lib/ tree is linted [skip ci]

    The unquoted lib/**/*.js globs in the lint script and Gruntfile were
    expanded by sh (no globstar) to depth-2 files only, so the 48 root-level
    lib files and everything three or more levels deep were never linted by
    npm test or CI. Quoting the globs lets eslint expand them itself.
    
    Fixes the issues this surfaced (all behavior-neutral):
    
    - lib/account.js: drop a dead initializer (no-useless-assignment)
    - lib/autodetect-imap-settings.js: remove a leftover dead filter statement
    - lib/email-client/imap/subconnection.js: remove a no-op null assignment
    - lib/email-client/imap/sync-operations.js, lib/stream-encrypt.js:
      attach the caught error as cause when rethrowing (preserve-caught-error)
    - lib/imapproxy/imap-core/lib/imap-connection.js: remove a dead assignment
    - lib/imapproxy/imap-core/test/imap-indexer-test.js: keep the smoke parse
      without the unused assignment
    - lib/metrics-collector.js, lib/redis-operations.js: drop unused variables
    
    The tenth surfaced error (lib/webhooks.js:401) was a real bug and is
    fixed separately.
    andris9 committed Jun 11, 2026
    Configuration menu
    Copy the full SHA
    1813f08 View commit details
    Browse the repository at this point in the history
  2. fix: resolve code review findings in Gmail bulk ops, exports, webhook…

    …s, and schemas
    
    Bugs:
    
    - Gmail API: resolve add/remove label conflicts in favor of the add in
      updateMessage/updateMessages, so bulk deletes from the Trash folder
      (add TRASH + remove TRASH) and moves within the current folder no
      longer fail with 400, and map IMAP special-use labels to Gmail label
      IDs in bulk updates via a shared toGmailLabelId helper (single-message
      updates already mapped them)
    - export worker: isSkippableError now reads the machine-readable code
      from Boom output payloads (like its sibling isFolderMissingError), so
      a message expunged mid-export is skipped instead of failing the whole
      export job; the error classifiers moved to lib/export.js for reuse
      and test coverage
    - webhooks: the handler cache version marker was assigned backwards and
      the per-route version compared a number to the string from Redis, so
      the cache was rebuilt from Redis on every webhook event once a custom
      route existed; the cache now refreshes only when routes change
    - settings: enabling generateEmailSummary or openAiGenerateEmbeddings
      now really enables notifyText - the old 'key in [array]' condition
      never matched - while still storing the key itself and bumping the
      OpenAI settings version
    - schemas: token access.ip and the short mailbox listing delimiter
      allow null, remoteAddress is documented as the IP of the client that
      created the token (not a restriction), and search responses document
      the documentStoreQuery debug field
    - gettext-extract: skip empty msgids that would corrupt the POT header
      entry
    
    Cleanup: shared mailbox base fields and a verify-result factory in the
    response schemas, shared OAuth2 scope-array schema, hoisted template
    account field, single schemas require in oauth2-app-routes, dropped the
    serviceUrl normalization from the settings route (settings.set already
    normalizes via formatSettingValue), removed unused schema exports and
    the dead module.exports block in workers/export.js.
    
    Tests: new regression coverage for the export error classifiers, the
    webhook handler cache, the settings AI/notifyText coupling, and gettext
    extraction (interpolated template literal tripwire, ngettext-only
    module coverage, empty msgid guard). translations/messages.pot
    regenerated for the final line references.
    andris9 committed Jun 11, 2026
    Configuration menu
    Copy the full SHA
    f0ddb46 View commit details
    Browse the repository at this point in the history
  3. feat: replace Bugsnag with self-hosted Sentry for error tracking

    Error tracking now uses @sentry/node gated on the SENTRY_DSN env var,
    replacing @bugsnag/js and BUGSNAG_API_KEY. The nine duplicated init
    blocks in server.js and the worker entry points are consolidated into
    lib/sentry.js. Sentry is configured for error capture only: the SDK is
    required lazily so disabled installs pay no startup cost, and default
    integrations plus OpenTelemetry setup are skipped to keep http/fetch
    hot paths unpatched.
    
    The global uncaughtException/unhandledRejection handlers in lib/logger.js
    now always terminate the process, reporting the error and waiting for a
    short delivery flush first when tracking is enabled. Previously exit was
    delegated to the tracking SDK, which Sentry does not guarantee in worker
    threads.
    andris9 committed Jun 11, 2026
    Configuration menu
    Copy the full SHA
    62de831 View commit details
    Browse the repository at this point in the history
  4. feat: allow enabling Sentry error reporting from the admin UI

    Adds sentryEnabled and sentryDsn settings, exposed on the Config ->
    Logging page and through /v1/settings. When enabled without a custom
    DSN, reports go to the shared community Sentry instance run by the
    EmailEngine developers, so error reporting can be switched on
    temporarily during a support case without setting up a Sentry server.
    
    The settings are applied at runtime: lib/sentry.js polls them once a
    minute in every thread and starts or stops the SDK on change, so no
    restart is needed. The SENTRY_DSN environment variable still pins the
    configuration and disables the runtime settings.
    andris9 committed Jun 11, 2026
    Configuration menu
    Copy the full SHA
    a4076a4 View commit details
    Browse the repository at this point in the history
  5. feat: tag Sentry error reports with instance id and license key

    Events from different EmailEngine installations sharing the community
    Sentry DSN are now attributable: every report carries an 'instance' tag
    (the unique serviceId of the installation) and a 'license' tag (the
    license key id, read from the cached license metadata). Identity values
    are read once per SDK start, so a license swapped at runtime shows up
    after the next restart. A failed identity lookup only means untagged
    events, it never breaks error reporting.
    andris9 committed Jun 11, 2026
    Configuration menu
    Copy the full SHA
    2e9683f View commit details
    Browse the repository at this point in the history
  6. Configuration menu
    Copy the full SHA
    36cbdb8 View commit details
    Browse the repository at this point in the history
  7. Configuration menu
    Copy the full SHA
    76e2a80 View commit details
    Browse the repository at this point in the history
  8. Merge pull request #606 from postalsys/release-please--branches--mast…

    …er--components--emailengine-app
    
    chore(master): release 2.70.0
    andris9 authored Jun 11, 2026
    Configuration menu
    Copy the full SHA
    303496c View commit details
    Browse the repository at this point in the history
Loading