diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aca5047 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +out/ +.react-email diff --git a/.react-email/emails/vercel-invite-user.tsx b/.react-email/emails/vercel-invite-user.tsx deleted file mode 100644 index 9caa3bf..0000000 --- a/.react-email/emails/vercel-invite-user.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import Mail from '../../emails/vercel-invite-user.tsx'; -export default Mail; \ No newline at end of file diff --git a/.react-email/public/static/notion-logo.png b/.react-email/public/static/notion-logo.png deleted file mode 100644 index 57cddee..0000000 Binary files a/.react-email/public/static/notion-logo.png and /dev/null differ diff --git a/.react-email/public/static/plaid-logo.png b/.react-email/public/static/plaid-logo.png deleted file mode 100644 index ce9d08c..0000000 Binary files a/.react-email/public/static/plaid-logo.png and /dev/null differ diff --git a/.react-email/public/static/plaid.png b/.react-email/public/static/plaid.png deleted file mode 100644 index ce9d08c..0000000 Binary files a/.react-email/public/static/plaid.png and /dev/null differ diff --git a/.react-email/public/static/stripe-logo.png b/.react-email/public/static/stripe-logo.png deleted file mode 100644 index af4c771..0000000 Binary files a/.react-email/public/static/stripe-logo.png and /dev/null differ diff --git a/.react-email/public/static/vercel-arrow.png b/.react-email/public/static/vercel-arrow.png deleted file mode 100644 index 018f64d..0000000 Binary files a/.react-email/public/static/vercel-arrow.png and /dev/null differ diff --git a/.react-email/public/static/vercel-logo.png b/.react-email/public/static/vercel-logo.png deleted file mode 100644 index 5b97094..0000000 Binary files a/.react-email/public/static/vercel-logo.png and /dev/null differ diff --git a/.react-email/public/static/vercel-team.png b/.react-email/public/static/vercel-team.png deleted file mode 100644 index d3de7d9..0000000 Binary files a/.react-email/public/static/vercel-team.png and /dev/null differ diff --git a/.react-email/public/static/vercel-user.png b/.react-email/public/static/vercel-user.png deleted file mode 100644 index 81beac6..0000000 Binary files a/.react-email/public/static/vercel-user.png and /dev/null differ diff --git a/data/comment.ts b/data/comment.ts new file mode 100644 index 0000000..237c4f0 --- /dev/null +++ b/data/comment.ts @@ -0,0 +1,59 @@ +import dayjs from 'dayjs' +export interface CommentModelRenderProps { + author: string + avatar: string + mail: string + text: string + ip?: string | undefined + agent: string + created: string + isWhispers: boolean + location?: string | undefined + url: string +} +const defaultCommentModelForRenderProps: CommentModelRenderProps = { + author: 'Commentor' as string, + avatar: + 'https://cloudflare-ipfs.com/ipfs/Qmd3W5DuhgHirLHGVixi6V76LhCkZUz6pnFt5AJBiyvHye/avatar/976.jpg' as string, + mail: 'commtor@example.com' as string, + text: `Tenetur ipsam quis illo eos pariatur. Minima rerum accusantium. Nesciunt facere illo reprehenderit qui voluptatem omnis temporibus consequatur similique. Et quidem vero aperiam iure cumque perspiciatis. Vitae occaecati quam non. Placeat officia iste voluptatibus magnam neque accusamus magni alias at. + Nobis quia enim error molestiae praesentium molestias. Itaque harum cupiditate itaque ea necessitatibus deleniti. Excepturi nihil voluptas vel vitae asperiores eveniet illum. Soluta veritatis quasi commodi. Enim laudantium quaerat recusandae nulla placeat. + Ad recusandae et sint impedit at esse illo exercitationem. Odit repellat non. Quisquam asperiores eveniet dolore. Magni iste modi at delectus. Pariatur consequuntur officia ab libero. Beatae voluptatum quia impedit deleniti ipsam eaque iure.` as string, + ip: '0.0.0.0' as string | undefined, + agent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' as string, + created: new Date().toISOString(), + isWhispers: false, + location: '' as string | undefined, + url: 'https://blog.commentor.com' as string, +} + +const defaultPostModelForRenderProps = { + title: '匆匆', + id: 'd7e0ed429da8ae90988c37da', + text: '燕子去了,有再来的时候;杨柳枯了,有再青的时候;桃花谢了,有再开的时候。但是,聪明的,你告诉我,我们的日子为什么一去不复返呢?——是有人偷了他们罢:那是谁?又藏在何处呢?是他们自己逃走了罢:如今(现在 [2] )又到了哪里呢?', + created: new Date().toISOString(), + modified: new Date().toISOString(), +} + +export const baseRenderProps = Object.freeze({ + author: defaultCommentModelForRenderProps.author, + link: 'https://innei.ren/note/122#comments-37ccbeec9c15bb0ddc51ca7d' as string, + + mail: defaultCommentModelForRenderProps.mail, + text: defaultCommentModelForRenderProps.text, + title: '文章的标题' as string, + time: dayjs().format('YYYY/MM/DD'), + master: 'Innei' as string, + + ip: defaultCommentModelForRenderProps.ip, + + aggregate: { + post: defaultPostModelForRenderProps, + commentor: defaultCommentModelForRenderProps, + owner: { + username: 'innei', + avatar: 'https://avatars.githubusercontent.com/u/41265413?v=4', + }, + }, +}) diff --git a/emails/guest-notification-comment.tsx b/emails/guest-notification-comment.tsx new file mode 100644 index 0000000..da28c53 --- /dev/null +++ b/emails/guest-notification-comment.tsx @@ -0,0 +1,92 @@ +import { + Body, + Button, + Container, + Head, + Heading, + Hr, + Html, + Img, + Preview, + Section, + Tailwind, + Text, +} from '@react-email/components' +import { get } from 'lodash-es' +import * as React from 'react' +import { baseRenderProps } from '../data/comment' + +const getEjsValue = (key: string) => + process.env.NODE_ENV !== 'development' + ? `<%= ${key} %>` + : get(baseRenderProps, key) +export const GuestReceivedEmailTemplate = () => { + const previewText = `${getEjsValue('master')} 在「${getEjsValue( + 'title', + )}」给你回复啦! ${getEjsValue('master')} 回复说: ${getEjsValue('text')}` + return ( + + + {previewText} + + + +
+ +
+
+ +
+ + 您在 『{getEjsValue('title')}』 + 的评论有了新的回复呐~ + + + {getEjsValue('master')} 给您的回复: + +
+ + {getEjsValue('text')} + +
+
+ +
+
+
+ + 萤火虫消失之后,那光的轨迹仍久久地印在我的脑际。那微弱浅淡的光点,仿佛迷失方向的魂灵,在漆黑厚重的夜幕中彷徨。——《挪威的森林》村上春树 + +
+ +
+ + 本邮件为系统自动发送,请勿直接回复~
© + {`<%= new Date().getFullYear() %> Copyright <%= master %>`} +
+
+
+ +
+ + ) +} + +export default GuestReceivedEmailTemplate diff --git a/emails/owner-notification-comment.tsx b/emails/owner-notification-comment.tsx new file mode 100644 index 0000000..11c074d --- /dev/null +++ b/emails/owner-notification-comment.tsx @@ -0,0 +1,109 @@ +import { + Body, + Button, + Container, + Head, + Heading, + Hr, + Html, + Img, + Preview, + Section, + Tailwind, + Text, +} from '@react-email/components' +import { get } from 'lodash-es' +import * as React from 'react' +import { baseRenderProps } from '../data/comment' + +const getEjsValue = (key: string) => + process.env.NODE_ENV !== 'development' + ? `<%= ${key} %>` + : get(baseRenderProps, key) +export const OwnerReceivedEmailTemplate = () => { + const previewText = `${getEjsValue('author')} 在《${getEjsValue( + 'title', + )}》说:${getEjsValue('text')}` + // const commentorAvatar = getEjsValue('aggregate.comment.avatar') + return ( + + + {previewText} + + + +
+ +
+
+ +
+ + 『{getEjsValue('title')}』 的评论收到了回复 + + + {getEjsValue('author')} 的回复: + +
+ + {getEjsValue('text')} + +
+ + + 其他信息: + +
+ + IP: {getEjsValue('ip')} +
+ Mail:{getEjsValue('aggregate.commentor.mail')} +
+ Agent: {getEjsValue('aggregate.commentor.agent')} +
+ HomePage: {getEjsValue('aggregate.commentor.url')} +
+
+ +
+ +
+
+
+ + 萤火虫消失之后,那光的轨迹仍久久地印在我的脑际。那微弱浅淡的光点,仿佛迷失方向的魂灵,在漆黑厚重的夜幕中彷徨。——《挪威的森林》村上春树 + +
+ +
+ + 本邮件为系统自动发送,请勿直接回复~
© + {`<%= new Date().getFullYear() %> Copyright <%= master %>`} +
+
+
+ +
+ + ) +} + +export default OwnerReceivedEmailTemplate diff --git a/emails/subscribe.tsx b/emails/subscribe.tsx new file mode 100644 index 0000000..f38fd5f --- /dev/null +++ b/emails/subscribe.tsx @@ -0,0 +1,119 @@ +import { + Body, + Button, + Container, + Head, + Heading, + Hr, + Html, + Img, + Link, + Preview, + Section, + Tailwind, + Text, +} from '@react-email/components' +import { get } from 'lodash-es' +import React from 'react' + +const defaultPostProps = { + text: '年纪在四十以上,二十以下的,恐怕就不易在前两派里有个地位了。他们的车破,又不敢“拉晚儿”,所以只能早早的出车,希望能从清晨转到午后三四点钟,拉出“车份儿”和自己的嚼谷①。他们的车破,跑得慢,所以得多走路,少要钱。到瓜市,果市,菜市,去拉货物,都是他们;钱少,可是无须快跑呢。', + title: '骆驼祥子', +} + +export const defaultSubscribeForRenderProps = { + ...defaultPostProps, + + author: '', + detail_link: '#detail_link', + unsubscribe_link: '#unsubscribe_link', + master: '', + + aggregate: { + owner: { + username: 'innei', + name: 'innei', + avatar: 'https://avatars.githubusercontent.com/u/41265413?v=4', + }, + subscriber: { + email: 'subscriber@mail.com', + }, + post: { + ...defaultPostProps, + id: 'cdab54a19f3f03f7f5159df7', + created: '2023-06-04T15:02:09.179Z', + }, + }, +} + +const getEjsValue = (key: string) => + process.env.NODE_ENV !== 'development' + ? `<%= ${key} %>` + : get(defaultSubscribeForRenderProps, key) + +export default () => ( + + + + 你关注的 @{getEjsValue('aggregate.owner')} 有新的内容发布啦。 + {`<%= aggregate.post.text.slice(0, 20) %>`} + + + + +
+ +
+
+ +
+ + + 你关注的 @{getEjsValue('aggregate.owner.name')} 刚刚发布了: + + + + {getEjsValue('title')} + + {getEjsValue('text')} + +
+ + + + 退订 + +
+ +
+
+ + 本邮件为系统自动发送,请勿直接回复~
© + {`<%= new Date().getFullYear() %> Copyright <%= aggregate.owner.name %>`} +
+
+
+ +
+ +) diff --git a/emails/vercel-invite-user.tsx b/emails/vercel-invite-user.tsx deleted file mode 100644 index 35eeff6..0000000 --- a/emails/vercel-invite-user.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { - Body, - Button, - Column, - Container, - Head, - Heading, - Hr, - Html, - Img, - Link, - Preview, - Row, - Section, - Tailwind, - Text, -} from '@react-email/components' -import * as React from 'react' - -const getEjsValue = (key: string) => `<%= ${key} %>` -export const GuestReceivedEmailTemplate = () => { - return ( - - - previewText - - - -
- Vercel -
- - Join teamName on Vercel - - Hello - - bukinoshita ( - invitation) - has invited you to the team team on{' '} - Vercel. - -
- - - - - - invited you to - - - - - -
-
- -
- - or copy and paste this URL into your browser:{' '} - inviteLink - -
- - This invitation was intended for{' '} - {'username'} .This invite was - sent from {'inviteFromIp'}{' '} - located in{' '} - {'inviteFromLocation'}. If you - were not expecting this invitation, you can ignore this email. If - you are concerned about your account's safety, please reply to - this email to get in touch with us. - -
- -
- - ) -} - -export default GuestReceivedEmailTemplate diff --git a/package.json b/package.json index 396b821..1fbe83d 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,19 @@ ], "scripts": { "dev": "email dev", - "export": "email export" + "export": "email export && node restore-ejs-tag.js" }, "dependencies": { "@react-email/components": "0.0.6", "react-email": "1.9.3" }, "devDependencies": { + "@types/lodash-es": "4.17.7", + "@types/node": "20.2.5", "@types/react": "18.2.8", - "react": "18.2.0" + "dayjs": "1.11.8", + "lodash-es": "4.17.21", + "react": "18.2.0", + "tailwind-gradient-mask-image": "1.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 14d54b4..9b74b2a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,12 +13,27 @@ dependencies: version: 1.9.3 devDependencies: + '@types/lodash-es': + specifier: 4.17.7 + version: 4.17.7 + '@types/node': + specifier: 20.2.5 + version: 20.2.5 '@types/react': specifier: 18.2.8 version: 18.2.8 + dayjs: + specifier: 1.11.8 + version: 1.11.8 + lodash-es: + specifier: 4.17.21 + version: 4.17.21 react: specifier: 18.2.0 version: 18.2.0 + tailwind-gradient-mask-image: + specifier: 1.0.0 + version: 1.0.0 packages: @@ -602,10 +617,24 @@ packages: selderee: 0.10.0 dev: false + /@types/lodash-es@4.17.7: + resolution: {integrity: sha512-z0ptr6UI10VlU6l5MYhGwS4mC8DZyYer2mCoyysZtSF7p26zOX8UpbrV0YpNYLGS8K4PUFIyEr62IMFFjveSiQ==} + dependencies: + '@types/lodash': 4.14.195 + dev: true + + /@types/lodash@4.14.195: + resolution: {integrity: sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==} + dev: true + /@types/node@12.20.55: resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} dev: false + /@types/node@20.2.5: + resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==} + dev: true + /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: false @@ -907,6 +936,10 @@ packages: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} dev: true + /dayjs@1.11.8: + resolution: {integrity: sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==} + dev: true + /deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -1489,6 +1522,10 @@ packages: p-locate: 4.1.0 dev: false + /lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: true + /log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} @@ -2135,6 +2172,10 @@ packages: engines: {node: '>= 0.4'} dev: false + /tailwind-gradient-mask-image@1.0.0: + resolution: {integrity: sha512-eZJhn6wHZ0Irfpq5sm0ErewH6IC82gqjfVsDQ7MYrJcfTgepOoTI6EryLppNPoZds4EeD6/H0WrysT8HE90H5g==} + dev: true + /tailwindcss@3.2.7(postcss@8.4.21): resolution: {integrity: sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==} engines: {node: '>=12.13.0'} diff --git a/restore-ejs-tag.js b/restore-ejs-tag.js new file mode 100644 index 0000000..e7ea78b --- /dev/null +++ b/restore-ejs-tag.js @@ -0,0 +1,40 @@ +const fs = require('fs') +const util = require('util') +const path = require('path') +const ejsTagsMap = { + '<%': '<%', + '<%=': '<%=', + '<%-': '<%-', + '%>': '%>', +} + +function restoreEjsTags(htmlStr) { + for (let key in ejsTagsMap) { + let regex = new RegExp(key, 'g') + htmlStr = htmlStr.replace(regex, ejsTagsMap[key]) + } + return htmlStr +} + +const readdir = util.promisify(fs.readdir) +const readFile = util.promisify(fs.readFile) +const writeFile = util.promisify(fs.writeFile) + +const outDir = './out' + +readdir(outDir) + .then((files) => { + files.forEach((file) => { + if (path.extname(file) === '.html') { + const filePath = path.join(outDir, file) + readFile(filePath, 'utf-8') + .then((data) => { + const restoredHtml = restoreEjsTags(data) + return writeFile(filePath, restoredHtml) + }) + .then(() => console.log(`File ${file} has been updated.`)) + .catch((err) => console.error(err)) + } + }) + }) + .catch((err) => console.error(err))