Skip to content

feat(cli): add openship mail install for the standalone webmail dashboard#141

Open
Rish-it wants to merge 2 commits into
oblien:mainfrom
Rish-it:feat/mail-standalone-webmail-install
Open

feat(cli): add openship mail install for the standalone webmail dashboard#141
Rish-it wants to merge 2 commits into
oblien:mainfrom
Rish-it:feat/mail-standalone-webmail-install

Conversation

@Rish-it

@Rish-it Rish-it commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

WIP / draft — first slice, to iterate on. Opening early for direction.

Refs #137.

What

Adds openship mail install — the CLI entry point for installing the standalone
mail (Zero webmail) dashboard, reusing the same machinery the integrated
dashboard wizard already uses. No new API, no reimplementation of the mail stack.

openship mail install <serverId> -d <domain> [-t mail|cloud|<serverId>] [--internal-port n] [--list-targets] [--no-watch]

How it maps to the "integrated version"

The integrated install is the dashboard wizard at apps/dashboard/.../apps/new/mail:

  • Self-host → the iRedMail provisioning flow — already exposed on the CLI as openship mail setup.
  • Standalone webmail dashboard → deploy the Zero webmail UI via POST /mail/webmail/deploy-project.

The CLI had no verb for the second path, even though mail.ts's own header already
documents the intended webmail targets + deploy-project surface. This wires it up.

Fix

apps/cli/src/commands/mail.ts (+97, one file):

  • --list-targetsGET /mail/webmail/targets?serverId= (the same picker the wizard reads).
  • deploy → POST /mail/webmail/deploy-project { mailServerId, hostname, target, internalPort? },
    then streams the build over the shared deployment SSE (streamDeploymentLogs), the same
    /build/[deploymentId] stream the dashboard subscribes to.
  • --target resolves mail (default, the mail server itself) / cloud / a server ID to the
    { kind: "self", serverId } | { kind: "cloud" } body.
  • Streams by default; --no-watch just queues. JSON mode honored throughout.

Also uses the previously-dead streamDeploymentLogs import in this file.

Tests

apps/cli has no test runner (lint is tsc --noEmit; the CLI isn't unit-tested).
Adding a framework for one command would be out of scope, so verification is the
strongest available, run twice:

  • tsc --noEmit → clean (twice).
  • Built binary: mail install registers under mail, --help renders, --list-targets
    present, missing --domain rejected. Re-verified against committed HEAD.

Scope / follow-ups (WIP)

  • Deferred: the external IMAP/SMTP path (POST /mail/webmail/deploy-external, the wizard's
    "connect existing" provider). Natural next step — same command, a --external mode.
  • Open to reshaping the flag surface / subcommand split before this leaves draft.

…hboard

Expose the webmail deploy flow the dashboard mail wizard already uses
(GET /mail/webmail/targets + POST /mail/webmail/deploy-project) as a
first-class CLI verb, streaming the build over the shared deployment SSE.
Deploys the standalone Zero webmail UI for an existing self-hosted mail
server; --list-targets lists valid hosts, --no-watch just queues.
--domain was a required option, so listing deploy targets — a read-only
discovery step run before a host is chosen — failed with "required option
'-d, --domain' not specified". Make --domain a plain option and enforce it
in the action after the --list-targets early return, so an actual install
still requires a domain while discovery does not.
@Rish-it

Rish-it commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Status + Request for a Live Deployment Check

Still WIP/draft, but the command is functionally complete for the self-hosted webmail flow. I have verified it end to end against the existing API contract.

Two commits:

  • feat(cli): add openship mail install
  • fix(cli): let mail install --list-targets run without --domain

What’s verified

The goal was to reuse the same functionality as the integrated dashboard version.

The validation bar was simple: the CLI must send the exact same request as the dashboard wizard.

I ran the built binary against a stub API and captured the HTTP request generated for every supported flag combination. In each case, the request body matched the payload sent by deploy/mail/page.tsx to POST /mail/webmail/deploy-project:

{
  "mailServerId": "srv_1",
  "hostname": "mail.example.com",
  "target": {
    "kind": "self",
    "serverId": "srv_1"
  }
}

Verified cases:

  • --target mail maps to { "kind": "self", "serverId": "<mailServerId>" }
  • --target <serverId> maps to { "kind": "self", "serverId": "<serverId>" }
  • --target cloud maps to { "kind": "cloud" }
  • --target opshcloud maps to { "kind": "cloud" }
  • --internal-port is serialized as a JSON number
  • Invalid ports are rejected client-side
  • --no-watch works
  • --json works
  • --list-targets reads from GET /mail/webmail/targets

No new API routes or deployment logic were added. The command uses the existing deploy-project endpoint and build-session SSE flow.

Bug found and fixed

While testing, I found that --domain was defined as a required option.

That caused this command to fail:

openship mail install <serverId> --list-targets

With:

required option '-d, --domain' not specified

This was incorrect because listing targets is a discovery step that should work before a hostname has been selected.

The fix:

  • Changed --domain from a required option to a regular option
  • Handled the --list-targets early return first
  • Added domain validation inside the action for actual install requests

A real installation still requires --domain; only --list-targets can run without it.

What I could not verify

I do not have access to a provisioned mail server, so I could not run a real deployment through to completion.

The following path is still unverified from the CLI:

Project creation
→ Preflight checks
→ Workspace transfer
→ OpenResty vhost configuration
→ Let’s Encrypt certificate issuance
→ bun run server/src/main.ts
→ Webmail becomes reachable

This path is shared with the dashboard wizard and is already exercised in production, so the implementation risk should be low. However, the CLI flow has not yet been tested against real hardware.

Request for a live deployment check

Could you run it once against a real mail server and flag anything that looks wrong?

cd apps/email
bun run build

openship mail install <serverId> --list-targets

openship mail install <serverId> \
  --domain webmail.<your-domain>

Things worth checking specifically:

  • Whether the OpenResty vhost and certificate setup complete successfully
  • Whether --target mail conflicts with any ports already used by iRedMail
  • Whether the deployed webmail can authenticate against the mail backend
  • Whether the build-session SSE stream reports progress and completion correctly
  • Whether the final webmail URL is reachable

I’m happy to fix anything uncovered during the live test.

The external IMAP/SMTP flow using deploy-external can be added separately as a follow-up through a deferred --external option.

Leaving this as a draft until the real-server deployment has been checked.

@Rish-it
Rish-it marked this pull request as ready for review July 23, 2026 11:00
Copilot AI review requested due to automatic review settings July 23, 2026 11:00
@Rish-it Rish-it changed the title feat(cli): add openship mail install for the standalone webmail dashboard (WIP) feat(cli): add openship mail install for the standalone webmail dashboard Jul 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants