Compare commits
No commits in common. "main" and "0.0.0-alpha-1" have entirely different histories.
main
...
0.0.0-alph
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
bin
|
||||
46
README.md
46
README.md
|
|
@ -2,48 +2,16 @@
|
|||
|
||||
Mail cli for Coh@bit.
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> Early preview Much more to come, stay up to date
|
||||
> [!WARNING] Early preview Much more to come, stay up to date
|
||||
|
||||
## 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]
|
||||
>
|
||||
> Currently bin manually added to [releases](./releases) tab. Prefer `git clone`
|
||||
> and [deno](https://deno.land) to use latest version.
|
||||
> [!WARNING] Currently bin manually added to [releases](./releases) tab.
|
||||
|
||||
## Basic usage
|
||||
|
||||
- From deno
|
||||
```sh
|
||||
cd ./cohabit_mail
|
||||
deno task cli -h
|
||||
deno task <subcommand> -h
|
||||
```
|
||||
|
||||
- From bin
|
||||
```sh
|
||||
./cohamail -h
|
||||
# OR
|
||||
./cohamail <subcommand> -h
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
You can override default config and templates with your own in
|
||||
`/etc/cohabit/mailer/(config|templates)/`.
|
||||
```sh
|
||||
./cohamail -h
|
||||
# OR
|
||||
./cohamail <subcommand> -h
|
||||
```
|
||||
|
|
|
|||
22
cli.ts
22
cli.ts
|
|
@ -1,34 +1,14 @@
|
|||
import config from './deno.json' with { type: 'json' }
|
||||
import { Command } from '@cliffy/command'
|
||||
import { UpgradeCommand } from '@cliffy/command/upgrade'
|
||||
import { JsrProvider } from '@cliffy/command/upgrade/provider/jsr'
|
||||
import { Command } from '@cliffy/command/command.ts'
|
||||
import { cmd as send } from './cli/send.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()
|
||||
.name('cohamail')
|
||||
.description('Mail cli for coh@bit.')
|
||||
.version(config.version)
|
||||
.command('preview', preview)
|
||||
.command('send', send)
|
||||
.command('upgrade', upgradeCommand)
|
||||
|
||||
if (import.meta.main) {
|
||||
if (Deno.args.length) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Input } from '@cliffy/prompt'
|
||||
import { Input } from '@cliffy/prompt/input.ts'
|
||||
import type { Template } from '../types.ts'
|
||||
import type { JSX } from 'preact'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { exists } from '@std/fs/exists'
|
||||
import { fromFileUrl } from '@std/path'
|
||||
import type { JSX } from 'preact'
|
||||
import { EnumType } from '@cliffy/command'
|
||||
import { EnumType } from '@cliffy/command/mod.ts'
|
||||
import type { Template } from '../types.ts'
|
||||
import * as defaultTemplates from '../templates/mod.ts'
|
||||
|
||||
export const templates: Map<
|
||||
string,
|
||||
|
|
@ -12,38 +11,25 @@ export const templates: Map<
|
|||
>
|
||||
> = new Map()
|
||||
|
||||
//Load default templates
|
||||
for (const template of Object.values(defaultTemplates)) {
|
||||
//@ts-expect-error types are checked at runtime later
|
||||
templates.set(template.name, template)
|
||||
}
|
||||
const templatesDirUrl = import.meta.resolve('../templates')
|
||||
const templatesDir = fromFileUrl(templatesDirUrl)
|
||||
|
||||
//Load local templates
|
||||
const basePath = '/etc/cohabit/mailer/templates'
|
||||
|
||||
if (await exists(basePath)) {
|
||||
for await (const template of Deno.readDir(basePath)) {
|
||||
if (!template.isFile) continue
|
||||
if (!template.name.endsWith('.tsx')) continue
|
||||
if (template.name.startsWith('_')) continue
|
||||
|
||||
const mod = await import(`${basePath}/${template.name}`).catch(
|
||||
checkFsErrors,
|
||||
//Load templates dynamicaly
|
||||
for await (
|
||||
const template of Deno.readDir(templatesDir)
|
||||
) {
|
||||
if (
|
||||
template.isFile &&
|
||||
template.name.endsWith('.tsx') &&
|
||||
!template.name.startsWith('_')
|
||||
) {
|
||||
const modPath = new URL(
|
||||
template.name,
|
||||
`${import.meta.resolve('../templates')}/`,
|
||||
)
|
||||
const mod = await import(modPath.href)
|
||||
templates.set(mod.default.name, mod.default)
|
||||
}
|
||||
}
|
||||
|
||||
function checkFsErrors(error: Error) {
|
||||
if (error instanceof Deno.errors.PermissionDenied) {
|
||||
throw new Error(
|
||||
'unable to load config file due to read access permissions issues',
|
||||
{
|
||||
cause: error,
|
||||
},
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
export const templateType = new EnumType([...templates.keys()])
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Command } from '@cliffy/command'
|
||||
import { Command } from '@cliffy/command/mod.ts'
|
||||
import { renderTemplate } from '../src/template.tsx'
|
||||
import { templates, templateType } from './_templates_loader.ts'
|
||||
import { promptProps } from './_prompt_template.ts'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Command } from '@cliffy/command'
|
||||
import { Command } from '@cliffy/command/mod.ts'
|
||||
import { Contact } from '../src/contact.ts'
|
||||
import { send } from '../src/send.ts'
|
||||
import type { Mail } from '../types.ts'
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"domainName": "cohabit.fr",
|
||||
"keySelector": "sendmailY2024M03",
|
||||
"privateKey": "/etc/ssl/certs/dkim_keys/dkimMailKey.pem"
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
import type { JsonValue } from '@std/json'
|
||||
import defaultAccounts from './account.json' with { type: 'json' }
|
||||
import defaultDkim from './dkim.json' with { type: 'json' }
|
||||
|
||||
function checkFsErrors(error: Error) {
|
||||
if (error instanceof Deno.errors.NotFound) {
|
||||
//use default config file
|
||||
return
|
||||
}
|
||||
if (error instanceof Deno.errors.PermissionDenied) {
|
||||
throw new Error(
|
||||
'unable to load config file due to read access permissions issues',
|
||||
{
|
||||
cause: error,
|
||||
},
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
type JsonRecord = Record<string, JsonValue>
|
||||
export type DkimRecord = {
|
||||
domainName: string
|
||||
keySelector: string
|
||||
privateKey: string
|
||||
}
|
||||
export type AccountRecord = Record<string, {
|
||||
name: string
|
||||
address: string
|
||||
}>
|
||||
|
||||
async function readJsonFile<
|
||||
T extends JsonRecord,
|
||||
>(path: string, defaultValue: T): Promise<T> {
|
||||
const file = await Deno.readTextFile(path).catch(checkFsErrors)
|
||||
const json = JSON.parse(file ?? '{}')
|
||||
return { ...defaultValue, ...json }
|
||||
}
|
||||
|
||||
const basePath = '/ect/cohabit/mailer/config'
|
||||
|
||||
export const accounts = await readJsonFile<AccountRecord>(
|
||||
`${basePath}/account.json`,
|
||||
defaultAccounts,
|
||||
)
|
||||
export const dkim = await readJsonFile<DkimRecord>(
|
||||
`${basePath}/dkim.json`,
|
||||
defaultDkim,
|
||||
)
|
||||
24
deno.json
24
deno.json
|
|
@ -1,15 +1,15 @@
|
|||
{
|
||||
"name": "@cohabit/mailer",
|
||||
"version": "0.5.2",
|
||||
"name": "@cohabit/cohamail",
|
||||
"version": "0.1.0",
|
||||
"exports": {
|
||||
".": "./mod.ts",
|
||||
"./cli": "./cli.ts",
|
||||
"./types": "./types.ts",
|
||||
"./templates": "./templates/mod.ts"
|
||||
"cli": "./cli.ts",
|
||||
"types": "./types.ts"
|
||||
},
|
||||
"tasks": {
|
||||
"compile": "deno compile --allow-read --allow-env --allow-net=0.0.0.0,jsr.io --allow-sys=osRelease,networkInterfaces --allow-run=/usr/sbin/sendmail,whoami --output=bin/mailer --target=x86_64-unknown-linux-gnu ./cli.ts",
|
||||
"cli": "deno run --allow-read --allow-env --allow-net=0.0.0.0,jsr.io --allow-sys=osRelease,networkInterfaces --allow-run=/usr/sbin/sendmail,whoami ./cli.ts"
|
||||
"compile:send": "deno compile --allow-read --allow-env --allow-sys=osRelease --allow-run=mail,whoami ./cli/send.ts",
|
||||
"cli": "deno run --allow-read --allow-env --allow-net=0.0.0.0 --allow-sys=osRelease,networkInterfaces --allow-run=/usr/sbin/sendmail,whoami ./cli.ts",
|
||||
"utils:scp": "scp -O -P 55555 -i C:/Users/Julien/.ssh/id_ed25519_cohabit -r $(pwd) julien@cohabit.fr:/home/julien/cohabit_mail"
|
||||
},
|
||||
"fmt": {
|
||||
"singleQuote": true,
|
||||
|
|
@ -17,11 +17,8 @@
|
|||
"useTabs": true
|
||||
},
|
||||
"imports": {
|
||||
"@cliffy/command": "jsr:@cliffy/command@^1.0.0-rc.5",
|
||||
"@cliffy/prompt": "jsr:@cliffy/prompt@^1.0.0-rc.5",
|
||||
"@std/fs": "jsr:@std/fs@^1.0.13",
|
||||
"@std/json": "jsr:@std/json@^1.0.1",
|
||||
"@types/nodemailer": "npm:@types/nodemailer@^6.4.15",
|
||||
"@cliffy/": "https://deno.land/x/cliffy@v1.0.0-rc.3/",
|
||||
"@std/path": "jsr:@std/path@^0.221.0",
|
||||
"jsx-email": "npm:jsx-email@^1.10.12",
|
||||
"nodemailer": "npm:nodemailer@^6.9.13",
|
||||
"nodemailer-smime": "npm:nodemailer-smime@^1.1.0",
|
||||
|
|
@ -31,6 +28,5 @@
|
|||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "preact"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
mod.ts
3
mod.ts
|
|
@ -1,5 +1,2 @@
|
|||
export { send } from './src/send.ts'
|
||||
export { Contact } from './src/contact.ts'
|
||||
export type { Mail, Template } from './types.ts'
|
||||
export { default as Message } from './templates/Message.tsx'
|
||||
export { default as Welcome } from './templates/Welcome.tsx'
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
import { accounts, dkim } from '../config/loader.ts'
|
||||
|
||||
export type AddressString = `${string}@${string}.${string}`
|
||||
import custom from '../config/account.json' with { type: 'json' }
|
||||
|
||||
export class Contact {
|
||||
#name: string
|
||||
#address: AddressString
|
||||
#address: `${string}@${string}.${string}`
|
||||
|
||||
constructor(
|
||||
{ name, address }: {
|
||||
name: string
|
||||
address: AddressString
|
||||
address: `${string}@${string}.${string}`
|
||||
},
|
||||
) {
|
||||
this.#name = name
|
||||
|
|
@ -17,11 +15,11 @@ export class Contact {
|
|||
}
|
||||
|
||||
static expand(shortName: string): Contact {
|
||||
if (!(shortName in accounts)) {
|
||||
if (!(shortName in custom)) {
|
||||
throw new Error('unknown short name contact')
|
||||
}
|
||||
|
||||
const { name, address } = accounts[shortName]
|
||||
const { name, address } = custom[shortName as keyof typeof custom]
|
||||
|
||||
if (typeof name !== 'string') {
|
||||
throw new SyntaxError(
|
||||
|
|
@ -33,9 +31,7 @@ export class Contact {
|
|||
`missing key "address" in contact short name config for "${shortName}"`,
|
||||
)
|
||||
}
|
||||
|
||||
const addressRegExp = new RegExp(String.raw`\w+@(\w+\.)?${dkim.domainName}`)
|
||||
if (!(addressRegExp.test(address))) {
|
||||
if (!(/\w+@(\w+\.)?cohabit\.fr/.test(address))) {
|
||||
throw new SyntaxError(
|
||||
`invalid "address" in contact short name config for "${shortName}"`,
|
||||
)
|
||||
|
|
@ -61,18 +57,18 @@ export class Contact {
|
|||
return `${this.#name} <${this.#address}>`
|
||||
}
|
||||
|
||||
toJSON(): { name: string; address: AddressString } {
|
||||
toJSON() {
|
||||
return {
|
||||
name: this.#name,
|
||||
address: this.#address,
|
||||
} as const
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
get name() {
|
||||
return this.#name
|
||||
}
|
||||
|
||||
get address(): AddressString {
|
||||
get address() {
|
||||
return this.#address
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
src/send.ts
14
src/send.ts
|
|
@ -1,11 +1,8 @@
|
|||
import type SendmailTransport from 'nodemailer'
|
||||
import type { Mail } from '../types.ts'
|
||||
import { renderTemplate } from './template.tsx'
|
||||
import { transporter } from './transporter.ts'
|
||||
|
||||
export async function send(
|
||||
mail: Mail,
|
||||
): Promise<SendmailTransport.SentMessageInfo> {
|
||||
export async function send(mail: Mail) {
|
||||
const { html, text } = await renderTemplate(mail.body)
|
||||
|
||||
return (await transporter()).sendMail({
|
||||
|
|
@ -16,6 +13,13 @@ export async function send(
|
|||
subject: mail.subject,
|
||||
text,
|
||||
html,
|
||||
attachments: mail.options.attachments.map((path) => ({ path })),
|
||||
attachments: [
|
||||
{
|
||||
cid: 'cohabit_logo.svg',
|
||||
path: 'https://cohabit.fr/images/logo.svg',
|
||||
filename: 'cohabit_logo.svg',
|
||||
},
|
||||
...mail.options.attachments.map((path) => ({ path })),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import React from 'preact/compat' //for jsx-email
|
||||
import { render } from 'jsx-email'
|
||||
// @deno-types="npm:turndown@^7.1.3"
|
||||
// @deno-types="npm:turndown"
|
||||
import Turndown from 'turndown'
|
||||
import type { JSX } from 'preact'
|
||||
|
||||
console.assert(React !== undefined)
|
||||
|
||||
const htmlToMd = new Turndown({
|
||||
headingStyle: 'atx',
|
||||
codeBlockStyle: 'fenced',
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// @deno-types="npm:@types/nodemailer@^6.4.15"
|
||||
// @deno-types="npm:@types/nodemailer"
|
||||
import nodemailer from 'nodemailer'
|
||||
import { dkim } from '../config/loader.ts'
|
||||
|
||||
export async function transporter() {
|
||||
const dkimPath = dkim.privateKey
|
||||
const dkimPath =
|
||||
'/home/julien/dkim_sendmail_keys/dkim_sendmail_cohabit_fr.pem'
|
||||
const dkimPrivateKey = await Deno.readTextFile(dkimPath).catch((cause) => {
|
||||
throw new Error(`unable to load DKIM private key from "${dkimPath}"`, {
|
||||
cause,
|
||||
|
|
@ -15,8 +15,8 @@ export async function transporter() {
|
|||
newline: 'unix',
|
||||
path: '/usr/sbin/sendmail',
|
||||
dkim: {
|
||||
domainName: dkim.domainName,
|
||||
keySelector: dkim.keySelector,
|
||||
domainName: 'cohabit.fr',
|
||||
keySelector: 'sendmailY2024M03',
|
||||
privateKey: dkimPrivateKey,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,140 +0,0 @@
|
|||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Heading,
|
||||
Html,
|
||||
Preview,
|
||||
Section,
|
||||
Text,
|
||||
} from 'jsx-email'
|
||||
import { Signature } from './components/Signature.tsx'
|
||||
import type { Template } from '../types.ts'
|
||||
import type { JSX } from 'preact'
|
||||
import {
|
||||
bodyCss,
|
||||
buttonCss,
|
||||
headingCss,
|
||||
messageCss,
|
||||
rootCss,
|
||||
textCss,
|
||||
} from './styles/base.tsx'
|
||||
import { BaseStyle } from './styles/base.tsx'
|
||||
|
||||
function MagicLink(
|
||||
{ message, device, ip, endpoint }: {
|
||||
message?: string
|
||||
device?: string
|
||||
ip?: string
|
||||
endpoint: string
|
||||
},
|
||||
): JSX.Element {
|
||||
return (
|
||||
<Html lang='fr' style={{ fontSize: '14px' }}>
|
||||
<BaseStyle />
|
||||
<Preview>Nouveau lien de connection</Preview>
|
||||
<Body style={bodyCss}>
|
||||
<Container style={messageCss}>
|
||||
<Section>
|
||||
<Heading as='h1' style={headingCss}>
|
||||
Nouveau lien de connection
|
||||
</Heading>
|
||||
<Text style={textCss}>
|
||||
Une nouvelle demande de connection à été effectuée sur votre
|
||||
compte.
|
||||
</Text>
|
||||
<Text style={detailsCss}>
|
||||
<span>
|
||||
<span style={{ fontWeight: 'bolder' }}>{'Date : '}</span>
|
||||
{dateNow()}
|
||||
</span>
|
||||
<br />
|
||||
<span>
|
||||
<span style={{ fontWeight: 'bolder' }}>{'Appareil : '}</span>
|
||||
{device?.length ? device : 'Iconnue'}
|
||||
</span>
|
||||
<br />
|
||||
<span>
|
||||
<span style={{ fontWeight: 'bolder' }}>{'Ip : '}</span>
|
||||
{ip?.length ? ip : 'Inconnue'}
|
||||
</span>
|
||||
</Text>
|
||||
{message?.length ? <Text style={textCss}>{message}</Text> : ''}
|
||||
<Text style={infoCss}>
|
||||
Si vous n'êtes pas à l'origine de cette demande vous pouvez
|
||||
ignorer ce mail en toute sécurité.
|
||||
</Text>
|
||||
|
||||
<Container
|
||||
style={{ width: '100%', textAlign: 'center', padding: '1rem' }}
|
||||
>
|
||||
<Button href={endpoint} style={buttonCss}>Me connecter</Button>
|
||||
</Container>
|
||||
</Section>
|
||||
</Container>
|
||||
<Signature />
|
||||
</Body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
|
||||
function dateNow() {
|
||||
return new Date().toLocaleString('fr', {
|
||||
weekday: 'long',
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
})
|
||||
}
|
||||
|
||||
const infoCss: JSX.CSSProperties = {
|
||||
...textCss,
|
||||
opacity: 0.7,
|
||||
}
|
||||
|
||||
const detailsCss: JSX.CSSProperties = {
|
||||
...textCss,
|
||||
backgroundColor: rootCss.backgroundColor,
|
||||
padding: '0.5rem',
|
||||
borderRadius: '0.4rem',
|
||||
}
|
||||
|
||||
const template: Template<typeof MagicLink, Parameters<typeof MagicLink>[0]> = {
|
||||
props: [
|
||||
{
|
||||
name: 'Message',
|
||||
description: "Message à afficher à l'utilisateur.",
|
||||
required: false,
|
||||
multiline: false,
|
||||
tag: 'message',
|
||||
},
|
||||
{
|
||||
name: 'Device',
|
||||
description: 'User-Agent (ou equiv.) de la demande.',
|
||||
required: false,
|
||||
multiline: false,
|
||||
tag: 'device',
|
||||
},
|
||||
{
|
||||
name: 'Ip',
|
||||
description: 'Addresse ip de la demande.',
|
||||
required: false,
|
||||
multiline: false,
|
||||
tag: 'ip',
|
||||
},
|
||||
{
|
||||
name: 'Endpoint',
|
||||
description: 'Endpoint du lien "Me connecter".',
|
||||
required: true,
|
||||
multiline: false,
|
||||
tag: 'endpoint',
|
||||
},
|
||||
],
|
||||
name: 'magic-link',
|
||||
description: 'Coh@bit connection magic link.',
|
||||
builder: MagicLink,
|
||||
}
|
||||
|
||||
export default template
|
||||
|
|
@ -2,11 +2,10 @@ import { Body, Container, Html, Markdown, Preview } from 'jsx-email'
|
|||
import { Signature } from './components/Signature.tsx'
|
||||
import type { Template } from '../types.ts'
|
||||
import { BaseStyle, bodyCss, messageCss, textCss } from './styles/base.tsx'
|
||||
import type { JSX } from 'preact'
|
||||
|
||||
function Message(
|
||||
{ summary, body }: { summary?: string; body: string },
|
||||
): JSX.Element {
|
||||
) {
|
||||
return (
|
||||
<Html lang='fr' style={{ fontSize: '14px' }}>
|
||||
<BaseStyle />
|
||||
|
|
|
|||
|
|
@ -12,24 +12,16 @@ import {
|
|||
import { Signature } from './components/Signature.tsx'
|
||||
import type { Template } from '../types.ts'
|
||||
import type { JSX } from 'preact'
|
||||
import {
|
||||
bodyCss,
|
||||
buttonCss,
|
||||
headingCss,
|
||||
messageCss,
|
||||
rootCss,
|
||||
textCss,
|
||||
} from './styles/base.tsx'
|
||||
import { bodyCss, messageCss, rootCss, textCss } from './styles/base.tsx'
|
||||
import { BaseStyle } from './styles/base.tsx'
|
||||
|
||||
function Welcome(
|
||||
{ firstname, lastname, login, endpoint }: {
|
||||
{ firstname, lastname, login }: {
|
||||
firstname: string
|
||||
lastname: string
|
||||
login: string
|
||||
endpoint?: string
|
||||
},
|
||||
): JSX.Element {
|
||||
) {
|
||||
return (
|
||||
<Html lang='fr' style={{ fontSize: '14px' }}>
|
||||
<BaseStyle />
|
||||
|
|
@ -39,7 +31,7 @@ function Welcome(
|
|||
<Section>
|
||||
<Heading as='h1' style={headingCss}>
|
||||
Bienvenue au<Img
|
||||
src='https://cohabit.fr/images/logo.png'
|
||||
src='cid:cohabit_logo.svg'
|
||||
alt='FabLab Cohabit'
|
||||
style={imgCss}
|
||||
/>
|
||||
|
|
@ -96,10 +88,7 @@ function Welcome(
|
|||
<Container
|
||||
style={{ width: '100%', textAlign: 'center', padding: '1rem' }}
|
||||
>
|
||||
<Button
|
||||
href={endpoint?.length ? endpoint : 'https://cohabit.fr/profil'}
|
||||
style={buttonCss}
|
||||
>
|
||||
<Button href='https://cohabit.fr/profil' style={buttonCss}>
|
||||
Accéder à mon compte
|
||||
</Button>
|
||||
</Container>
|
||||
|
|
@ -111,6 +100,14 @@ function Welcome(
|
|||
)
|
||||
}
|
||||
|
||||
const headingCss: JSX.CSSProperties = {
|
||||
fontFamily: 'Garamond, serif',
|
||||
color: rootCss.accentColor,
|
||||
textAlign: 'center',
|
||||
margin: '-1rem 0 3rem',
|
||||
fontSize: '2.5rem',
|
||||
}
|
||||
|
||||
const preCss: JSX.CSSProperties = {
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '1rem',
|
||||
|
|
@ -119,11 +116,18 @@ const preCss: JSX.CSSProperties = {
|
|||
backgroundColor: rootCss.backgroundColor,
|
||||
}
|
||||
|
||||
const buttonCss: JSX.CSSProperties = {
|
||||
color: 'white',
|
||||
padding: '1rem',
|
||||
borderRadius: '0.4rem',
|
||||
fontSize: '1.2rem',
|
||||
backgroundColor: rootCss.accentColor,
|
||||
}
|
||||
|
||||
const imgCss: JSX.CSSProperties = {
|
||||
display: 'inline',
|
||||
padding: '1rem',
|
||||
transform: 'translateY(40%)',
|
||||
maxHeight: '3rem',
|
||||
}
|
||||
|
||||
const template: Template<typeof Welcome, Parameters<typeof Welcome>[0]> = {
|
||||
|
|
@ -149,13 +153,6 @@ const template: Template<typeof Welcome, Parameters<typeof Welcome>[0]> = {
|
|||
multiline: false,
|
||||
tag: 'login',
|
||||
},
|
||||
{
|
||||
name: 'Endpoint',
|
||||
description: 'Endpoint du lien "Accéder à mon compte"',
|
||||
required: false,
|
||||
multiline: false,
|
||||
tag: 'endpoint',
|
||||
},
|
||||
],
|
||||
name: 'welcome',
|
||||
description: 'Coh@bit welcome mail for new users.',
|
||||
|
|
|
|||
|
|
@ -1,12 +1,4 @@
|
|||
import { Column, Container, Img, Row, Text } from 'jsx-email'
|
||||
import type { JSX } from 'preact/jsx-runtime'
|
||||
|
||||
const imgCss: JSX.CSSProperties = {
|
||||
display: 'inline',
|
||||
padding: '1rem',
|
||||
paddingBottom: 0,
|
||||
height: '3rem',
|
||||
}
|
||||
import { Column, Container, Row, Text } from 'jsx-email'
|
||||
|
||||
export function Signature() {
|
||||
return (
|
||||
|
|
@ -22,13 +14,6 @@ export function Signature() {
|
|||
<a href='mailto:fablab@iut.u-bordeaux.fr'>Nous contacter →</a>
|
||||
</Column>
|
||||
</Row>
|
||||
<Row>
|
||||
<Img
|
||||
src='https://cohabit.fr/images/logo.png'
|
||||
alt='FabLab Cohabit'
|
||||
style={imgCss}
|
||||
/>
|
||||
</Row>
|
||||
<Text style={{ fontSize: '12px' }}>
|
||||
Coh@bit, IUT de Bordeaux, Bâtiment 10A, 15 rue Naudet, 33170 GRADIGNAN
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
export { default as magicLinkTemplate } from './MagicLink.tsx'
|
||||
export { default as messageTemplate } from './Message.tsx'
|
||||
export { default as welcomeTemplate } from './Welcome.tsx'
|
||||
|
|
@ -19,26 +19,10 @@ export const messageCss: JSX.CSSProperties = {
|
|||
textWrap: 'balance',
|
||||
}
|
||||
|
||||
export const headingCss: JSX.CSSProperties = {
|
||||
fontFamily: 'Garamond, serif',
|
||||
color: rootCss.accentColor,
|
||||
textAlign: 'center',
|
||||
margin: '-1rem 0 3rem',
|
||||
fontSize: '2.5rem',
|
||||
}
|
||||
|
||||
export const textCss: JSX.CSSProperties = {
|
||||
fontSize: '1rem',
|
||||
}
|
||||
|
||||
export const buttonCss: JSX.CSSProperties = {
|
||||
color: 'white',
|
||||
padding: '1rem',
|
||||
borderRadius: '0.4rem',
|
||||
fontSize: '1.2rem',
|
||||
backgroundColor: rootCss.accentColor,
|
||||
}
|
||||
|
||||
export const rawCss = `
|
||||
a {
|
||||
color: ${rootCss.accentColor};
|
||||
|
|
|
|||
Loading…
Reference in a new issue