Files
lynkeduppro-crm/src/lib/smtp-settings-api.ts
T
maaz519 c874a726ef feat(settings): Email (SMTP) BYO card in Settings → Integrations
Replace the SMTP 'coming soon' stub with a real card: host/port/SSL/user/password/
from-address/from-name → crm.settings.smtp.configure, masked status from
crm.settings.smtp.status. Mirrors the Twilio SMS card; demo mode stores hints locally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 13:57:45 +05:30

90 lines
2.9 KiB
TypeScript

"use client";
// SMTP settings data layer — a tenant's own outbound email server (BYO). Serves the local mock
// (Shell not configured) or the live be-crm data door (crm.settings.smtp.*).
//
// Live contract (be-crm → IIOS BYO credential store):
// query crm.settings.smtp.status {} -> { configured, enabled?, hints? }
// cmd crm.settings.smtp.configure { host, port, secure, user, pass, fromEmail, fromName? } -> masked status
// The password is write-only: sealed in IIOS, never returned — status carries only non-secret hints.
import { useCallback, useState } from "react";
import { useAppShell, useQuery } from "@abe-kap/appshell-sdk/react";
import { isShellConfigured } from "./appshell";
export interface SmtpCredentials {
host: string;
port: number;
secure: boolean;
user: string;
pass: string;
fromEmail: string;
fromName?: string;
}
export interface SmtpStatus {
configured: boolean;
enabled: boolean;
host?: string;
port?: number;
user?: string;
fromEmail?: string;
fromName?: string;
}
export interface SmtpSettingsData {
live: boolean;
loading: boolean;
error: string | null;
status: SmtpStatus;
configure: (input: SmtpCredentials) => Promise<void>;
refetch: () => void;
}
interface StatusDTO { configured: boolean; enabled?: boolean; hints?: { host?: string; port?: number; user?: string; fromEmail?: string; fromName?: string } }
function toStatus(dto?: StatusDTO | null): SmtpStatus {
return {
configured: !!dto?.configured,
enabled: dto?.enabled ?? false,
host: dto?.hints?.host,
port: dto?.hints?.port,
user: dto?.hints?.user,
fromEmail: dto?.hints?.fromEmail,
fromName: dto?.hints?.fromName,
};
}
/* ---- Mock (demo mode) — stores only the non-secret hints ---- */
function useMockSmtp(): SmtpSettingsData {
const [status, setStatus] = useState<SmtpStatus>({ configured: false, enabled: false });
const configure = useCallback(async ({ host, port, user, fromEmail, fromName }: SmtpCredentials) => {
setStatus({ configured: true, enabled: true, host, port, user, fromEmail, ...(fromName ? { fromName } : {}) });
}, []);
return { live: false, loading: false, error: null, status, configure, refetch: () => {} };
}
/* ---- Live (be-crm data door) ---- */
function useLiveSmtp(): SmtpSettingsData {
const { sdk } = useAppShell();
const q = useQuery<StatusDTO>("crm.settings.smtp.status", {});
const configure = useCallback(async (input: SmtpCredentials) => {
await sdk.command("crm.settings.smtp.configure", { ...input });
q.refetch();
}, [sdk, q]);
return {
live: true,
loading: q.loading,
error: q.error ? String(q.error) : null,
status: toStatus(q.data),
configure,
refetch: q.refetch,
};
}
const SHELL = isShellConfigured();
export function useSmtpSettings(): SmtpSettingsData {
return SHELL ? useLiveSmtp() : useMockSmtp();
}