Compare commits

..

7 commits

10 changed files with 57 additions and 10 deletions

View file

@ -8,6 +8,20 @@ Mail cli for Coh@bit.
## Installation ## Installation
### From [jsr](https://jsr.io)
```sh
deno install \
--allow-read \
--allow-env \
--allow-net=0.0.0.0 \
--allow-sys=osRelease,networkInterfaces \
--allow-run=/usr/sbin/sendmail,whoami \
--global --force --name=cohamail jsr:@cohabit/mailer/cli
```
### From sources
> [!WARNING] > [!WARNING]
> >
> Currently bin manually added to [releases](./releases) tab. Prefer `git clone` > Currently bin manually added to [releases](./releases) tab. Prefer `git clone`

20
cli.ts
View file

@ -1,14 +1,34 @@
import config from './deno.json' with { type: 'json' } import config from './deno.json' with { type: 'json' }
import { Command } from '@cliffy/command' import { Command } from '@cliffy/command'
import { UpgradeCommand } from '@cliffy/command/upgrade'
import { JsrProvider } from '@cliffy/command/upgrade/provider/jsr'
import { cmd as send } from './cli/send.ts' import { cmd as send } from './cli/send.ts'
import { cmd as preview } from './cli/preview.ts' import { cmd as preview } from './cli/preview.ts'
const upgradeCommand = new UpgradeCommand({
args: [
'--allow-read',
'--allow-env',
'--allow-net=0.0.0.0',
'--allow-sys=osRelease,networkInterfaces',
'--allow-run=/usr/sbin/sendmail,whoami',
'--name=cohamail',
],
provider: [
new JsrProvider({
package: config.name as `@${string}/${string}`,
main: 'cli',
}),
],
})
const cli = new Command() const cli = new Command()
.name('cohamail') .name('cohamail')
.description('Mail cli for coh@bit.') .description('Mail cli for coh@bit.')
.version(config.version) .version(config.version)
.command('preview', preview) .command('preview', preview)
.command('send', send) .command('send', send)
.command('upgrade', upgradeCommand)
if (import.meta.main) { if (import.meta.main) {
if (Deno.args.length) { if (Deno.args.length) {

View file

@ -1,6 +1,6 @@
{ {
"name": "@cohabit/mailer", "name": "@cohabit/mailer",
"version": "0.2.1", "version": "0.3.0",
"exports": { "exports": {
".": "./mod.ts", ".": "./mod.ts",
"./cli": "./cli.ts", "./cli": "./cli.ts",

View file

@ -14,6 +14,7 @@
"jsr:@std/cli@1.0.0-rc.2": "jsr:@std/cli@1.0.0-rc.2", "jsr:@std/cli@1.0.0-rc.2": "jsr:@std/cli@1.0.0-rc.2",
"jsr:@std/encoding@1.0.0-rc.2": "jsr:@std/encoding@1.0.0-rc.2", "jsr:@std/encoding@1.0.0-rc.2": "jsr:@std/encoding@1.0.0-rc.2",
"jsr:@std/fmt@~0.225.4": "jsr:@std/fmt@0.225.6", "jsr:@std/fmt@~0.225.4": "jsr:@std/fmt@0.225.6",
"jsr:@std/io@~0.224.2": "jsr:@std/io@0.224.3",
"jsr:@std/path@1.0.0-rc.2": "jsr:@std/path@1.0.0-rc.2", "jsr:@std/path@1.0.0-rc.2": "jsr:@std/path@1.0.0-rc.2",
"jsr:@std/path@^0.221.0": "jsr:@std/path@0.221.0", "jsr:@std/path@^0.221.0": "jsr:@std/path@0.221.0",
"jsr:@std/text@1.0.0-rc.1": "jsr:@std/text@1.0.0-rc.1", "jsr:@std/text@1.0.0-rc.1": "jsr:@std/text@1.0.0-rc.1",
@ -59,6 +60,7 @@
"jsr:@cliffy/keycode@1.0.0-rc.5", "jsr:@cliffy/keycode@1.0.0-rc.5",
"jsr:@std/assert@1.0.0-rc.2", "jsr:@std/assert@1.0.0-rc.2",
"jsr:@std/fmt@~0.225.4", "jsr:@std/fmt@~0.225.4",
"jsr:@std/io@~0.224.2",
"jsr:@std/path@1.0.0-rc.2", "jsr:@std/path@1.0.0-rc.2",
"jsr:@std/text@1.0.0-rc.1" "jsr:@std/text@1.0.0-rc.1"
] ]
@ -84,6 +86,9 @@
"@std/fmt@0.225.6": { "@std/fmt@0.225.6": {
"integrity": "aba6aea27f66813cecfd9484e074a9e9845782ab0685c030e453a8a70b37afc8" "integrity": "aba6aea27f66813cecfd9484e074a9e9845782ab0685c030e453a8a70b37afc8"
}, },
"@std/io@0.224.3": {
"integrity": "b402edeb99c6b3778d9ae3e9927bc9085b170b41e5a09bbb7064ab2ee394ae2f"
},
"@std/path@0.221.0": { "@std/path@0.221.0": {
"integrity": "0a36f6b17314ef653a3a1649740cc8db51b25a133ecfe838f20b79a56ebe0095", "integrity": "0a36f6b17314ef653a3a1649740cc8db51b25a133ecfe838f20b79a56ebe0095",
"dependencies": [ "dependencies": [

View file

@ -1,14 +1,16 @@
import accounts from '../config/account.json' with { type: 'json' } import accounts from '../config/account.json' with { type: 'json' }
import dkim from '../config/dkim.json' with { type: 'json' } import dkim from '../config/dkim.json' with { type: 'json' }
export type AddressString = `${string}@${string}.${string}`
export class Contact { export class Contact {
#name: string #name: string
#address: `${string}@${string}.${string}` #address: AddressString
constructor( constructor(
{ name, address }: { { name, address }: {
name: string name: string
address: `${string}@${string}.${string}` address: AddressString
}, },
) { ) {
this.#name = name this.#name = name
@ -60,18 +62,18 @@ export class Contact {
return `${this.#name} <${this.#address}>` return `${this.#name} <${this.#address}>`
} }
toJSON() { toJSON(): { name: string; address: AddressString } {
return { return {
name: this.#name, name: this.#name,
address: this.#address, address: this.#address,
} as const } as const
} }
get name() { get name(): string {
return this.#name return this.#name
} }
get address() { get address(): AddressString {
return this.#address return this.#address
} }
} }

View file

@ -1,8 +1,11 @@
import type SendmailTransport from 'npm:@types/nodemailer'
import type { Mail } from '../types.ts' import type { Mail } from '../types.ts'
import { renderTemplate } from './template.tsx' import { renderTemplate } from './template.tsx'
import { transporter } from './transporter.ts' import { transporter } from './transporter.ts'
export async function send(mail: Mail) { export async function send(
mail: Mail,
): Promise<SendmailTransport.SentMessageInfo> {
const { html, text } = await renderTemplate(mail.body) const { html, text } = await renderTemplate(mail.body)
return (await transporter()).sendMail({ return (await transporter()).sendMail({

View file

@ -4,6 +4,8 @@ import { render } from 'jsx-email'
import Turndown from 'turndown' import Turndown from 'turndown'
import type { JSX } from 'preact' import type { JSX } from 'preact'
console.assert(React !== undefined)
const htmlToMd = new Turndown({ const htmlToMd = new Turndown({
headingStyle: 'atx', headingStyle: 'atx',
codeBlockStyle: 'fenced', codeBlockStyle: 'fenced',

View file

@ -28,7 +28,7 @@ function MagicLink(
ip?: string ip?: string
endpoint: string endpoint: string
}, },
) { ): JSX.Element {
return ( return (
<Html lang='fr' style={{ fontSize: '14px' }}> <Html lang='fr' style={{ fontSize: '14px' }}>
<BaseStyle /> <BaseStyle />

View file

@ -2,10 +2,11 @@ import { Body, Container, Html, Markdown, Preview } from 'jsx-email'
import { Signature } from './components/Signature.tsx' import { Signature } from './components/Signature.tsx'
import type { Template } from '../types.ts' import type { Template } from '../types.ts'
import { BaseStyle, bodyCss, messageCss, textCss } from './styles/base.tsx' import { BaseStyle, bodyCss, messageCss, textCss } from './styles/base.tsx'
import type { JSX } from 'preact'
function Message( function Message(
{ summary, body }: { summary?: string; body: string }, { summary, body }: { summary?: string; body: string },
) { ): JSX.Element {
return ( return (
<Html lang='fr' style={{ fontSize: '14px' }}> <Html lang='fr' style={{ fontSize: '14px' }}>
<BaseStyle /> <BaseStyle />

View file

@ -29,7 +29,7 @@ function Welcome(
login: string login: string
endpoint?: string endpoint?: string
}, },
) { ): JSX.Element {
return ( return (
<Html lang='fr' style={{ fontSize: '14px' }}> <Html lang='fr' style={{ fontSize: '14px' }}>
<BaseStyle /> <BaseStyle />