mirror of
https://github.com/docmost/docmost.git
synced 2026-05-08 15:23:07 +08:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c9fa6e20b3 | |||
| ec51ca7815 | |||
| 2b63137217 | |||
| 3227bc6059 | |||
| 73dc62bca3 | |||
| 3c74bb3dee | |||
| dbe6c2d6ba | |||
| fe18f22dc6 | |||
| fcef0c6b96 |
@@ -870,6 +870,8 @@
|
|||||||
"Previous 7 days": "Previous 7 days",
|
"Previous 7 days": "Previous 7 days",
|
||||||
"Previous 30 days": "Previous 30 days",
|
"Previous 30 days": "Previous 30 days",
|
||||||
"Search chats...": "Search chats...",
|
"Search chats...": "Search chats...",
|
||||||
|
"Search chats": "Search chats",
|
||||||
|
"Ask anything... Use @ to mention pages": "Ask anything... Use @ to mention pages",
|
||||||
"Start a new chat to see it here.": "Start a new chat to see it here.",
|
"Start a new chat to see it here.": "Start a new chat to see it here.",
|
||||||
"Summarize this page": "Summarize this page",
|
"Summarize this page": "Summarize this page",
|
||||||
"Toggle AI Chat": "Toggle AI Chat",
|
"Toggle AI Chat": "Toggle AI Chat",
|
||||||
|
|||||||
@@ -137,7 +137,8 @@ export default function AiChatSidebar() {
|
|||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
className={classes.searchInput}
|
className={classes.searchInput}
|
||||||
placeholder="Search chats..."
|
placeholder={t("Search chats...")}
|
||||||
|
aria-label={t("Search chats")}
|
||||||
leftSection={<IconSearch size={14} />}
|
leftSection={<IconSearch size={14} />}
|
||||||
size="xs"
|
size="xs"
|
||||||
value={search}
|
value={search}
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ export default function AsideChatPanel() {
|
|||||||
href="/ai"
|
href="/ai"
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
color="dark"
|
color="dark"
|
||||||
|
aria-label={t("New chat")}
|
||||||
onClick={handleNewChat}
|
onClick={handleNewChat}
|
||||||
>
|
>
|
||||||
<IconPlus size={20} stroke={1.75} />
|
<IconPlus size={20} stroke={1.75} />
|
||||||
@@ -185,13 +186,23 @@ export default function AsideChatPanel() {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip label={t("Open full page")} openDelay={250}>
|
<Tooltip label={t("Open full page")} openDelay={250}>
|
||||||
<ActionIcon variant="subtle" color="dark" onClick={handleExpand}>
|
<ActionIcon
|
||||||
|
variant="subtle"
|
||||||
|
color="dark"
|
||||||
|
aria-label={t("Open full page")}
|
||||||
|
onClick={handleExpand}
|
||||||
|
>
|
||||||
<IconArrowsDiagonal size={18} stroke={1.5} />
|
<IconArrowsDiagonal size={18} stroke={1.5} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip label={t("Close")} openDelay={250}>
|
<Tooltip label={t("Close")} openDelay={250}>
|
||||||
<ActionIcon variant="subtle" color="dark" onClick={handleClose}>
|
<ActionIcon
|
||||||
|
variant="subtle"
|
||||||
|
color="dark"
|
||||||
|
aria-label={t("Close")}
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
<IconX size={20} stroke={1.75} />
|
<IconX size={20} stroke={1.75} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -225,6 +225,10 @@ export default function ChatInput({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
editorProps: {
|
editorProps: {
|
||||||
|
attributes: {
|
||||||
|
"aria-label": placeholder || t("Ask anything... Use @ to mention pages"),
|
||||||
|
"aria-multiline": "true",
|
||||||
|
},
|
||||||
handleDOMEvents: {
|
handleDOMEvents: {
|
||||||
keydown: (_view, event) => {
|
keydown: (_view, event) => {
|
||||||
if (
|
if (
|
||||||
@@ -275,6 +279,8 @@ export default function ChatInput({
|
|||||||
type="file"
|
type="file"
|
||||||
accept={ACCEPTED_FILE_TYPES}
|
accept={ACCEPTED_FILE_TYPES}
|
||||||
multiple
|
multiple
|
||||||
|
aria-label={t("Add files")}
|
||||||
|
tabIndex={-1}
|
||||||
style={{ display: "none" }}
|
style={{ display: "none" }}
|
||||||
onChange={(e) => handleFileSelect(e.target.files)}
|
onChange={(e) => handleFileSelect(e.target.files)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -31,7 +31,16 @@ export default function ChatToolGroup({ toolCalls, isStreaming }: Props) {
|
|||||||
<div className={classes.toolGroup}>
|
<div className={classes.toolGroup}>
|
||||||
<div
|
<div
|
||||||
className={classes.toolGroupHeader}
|
className={classes.toolGroupHeader}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
aria-expanded={expanded}
|
||||||
onClick={() => setExpanded((prev) => !prev)}
|
onClick={() => setExpanded((prev) => !prev)}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === "Enter" || event.key === " ") {
|
||||||
|
event.preventDefault();
|
||||||
|
setExpanded((prev) => !prev);
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{activeLabel ? (
|
{activeLabel ? (
|
||||||
<IconLoader2 size={12} className={classes.processingSpinner} />
|
<IconLoader2 size={12} className={classes.processingSpinner} />
|
||||||
|
|||||||
@@ -98,7 +98,7 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
color: var(--mantine-color-dimmed);
|
||||||
margin-bottom: var(--mantine-spacing-xs);
|
margin-bottom: var(--mantine-spacing-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
.suggestionsLabel {
|
.suggestionsLabel {
|
||||||
font-size: var(--mantine-font-size-xs);
|
font-size: var(--mantine-font-size-xs);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
color: var(--mantine-color-dimmed);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
margin-bottom: var(--mantine-spacing-sm);
|
margin-bottom: var(--mantine-spacing-sm);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: var(--mantine-font-size-xs);
|
font-size: var(--mantine-font-size-xs);
|
||||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
color: var(--mantine-color-dimmed);
|
||||||
}
|
}
|
||||||
|
|
||||||
.attachmentChips {
|
.attachmentChips {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
padding: 4px var(--mantine-spacing-xs);
|
padding: 4px var(--mantine-spacing-xs);
|
||||||
font-size: var(--mantine-font-size-xs);
|
font-size: var(--mantine-font-size-xs);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
color: var(--mantine-color-dimmed);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
|
|
||||||
.chatItemDate {
|
.chatItemDate {
|
||||||
font-size: var(--mantine-font-size-xs);
|
font-size: var(--mantine-font-size-xs);
|
||||||
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
color: var(--mantine-color-dimmed);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
transition: opacity 150ms;
|
transition: opacity 150ms;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ const CommandGroups: SlashMenuGroupedItemsType = {
|
|||||||
{
|
{
|
||||||
title: "Numbered list",
|
title: "Numbered list",
|
||||||
description: "Create a list with numbering.",
|
description: "Create a list with numbering.",
|
||||||
searchTerms: ["numbered", "ordered", "list"],
|
searchTerms: ["numbered", "ordered", "list", "ol"],
|
||||||
icon: IconListNumbers,
|
icon: IconListNumbers,
|
||||||
command: ({ editor, range }: CommandProps) => {
|
command: ({ editor, range }: CommandProps) => {
|
||||||
editor.chain().focus().deleteRange(range).toggleOrderedList().run();
|
editor.chain().focus().deleteRange(range).toggleOrderedList().run();
|
||||||
@@ -471,7 +471,14 @@ const CommandGroups: SlashMenuGroupedItemsType = {
|
|||||||
{
|
{
|
||||||
title: "Subpages (Child pages)",
|
title: "Subpages (Child pages)",
|
||||||
description: "List all subpages of the current page",
|
description: "List all subpages of the current page",
|
||||||
searchTerms: ["subpages", "child", "children", "nested", "hierarchy"],
|
searchTerms: [
|
||||||
|
"subpages",
|
||||||
|
"child",
|
||||||
|
"children",
|
||||||
|
"nested",
|
||||||
|
"hierarchy",
|
||||||
|
"toc",
|
||||||
|
],
|
||||||
icon: IconSitemap,
|
icon: IconSitemap,
|
||||||
command: ({ editor, range }: CommandProps) => {
|
command: ({ editor, range }: CommandProps) => {
|
||||||
editor.chain().focus().deleteRange(range).insertSubpages().run();
|
editor.chain().focus().deleteRange(range).insertSubpages().run();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
font-size: var(--mantine-font-size-sm);
|
font-size: var(--mantine-font-size-sm);
|
||||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
color: var(--mantine-color-dimmed);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
margin-bottom: var(--mantine-spacing-lg);
|
margin-bottom: var(--mantine-spacing-lg);
|
||||||
|
|||||||
@@ -63,8 +63,6 @@
|
|||||||
"@nestjs/throttler": "^6.5.0",
|
"@nestjs/throttler": "^6.5.0",
|
||||||
"@nestjs/websockets": "^11.1.19",
|
"@nestjs/websockets": "^11.1.19",
|
||||||
"@node-saml/passport-saml": "^5.1.0",
|
"@node-saml/passport-saml": "^5.1.0",
|
||||||
"@react-email/components": "1.0.10",
|
|
||||||
"@react-email/render": "2.0.4",
|
|
||||||
"@socket.io/redis-adapter": "^8.3.0",
|
"@socket.io/redis-adapter": "^8.3.0",
|
||||||
"ai": "^6.0.134",
|
"ai": "^6.0.134",
|
||||||
"ai-sdk-ollama": "^3.8.1",
|
"ai-sdk-ollama": "^3.8.1",
|
||||||
@@ -108,6 +106,7 @@
|
|||||||
"postgres": "^3.4.8",
|
"postgres": "^3.4.8",
|
||||||
"postmark": "^4.0.7",
|
"postmark": "^4.0.7",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
|
"react-email": "6.0.8",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
@@ -146,7 +145,6 @@
|
|||||||
"jest": "^30.3.0",
|
"jest": "^30.3.0",
|
||||||
"kysely-codegen": "^0.20.0",
|
"kysely-codegen": "^0.20.0",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.1",
|
||||||
"react-email": "5.2.10",
|
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"supertest": "^7.2.2",
|
"supertest": "^7.2.2",
|
||||||
"ts-jest": "^29.4.6",
|
"ts-jest": "^29.4.6",
|
||||||
|
|||||||
@@ -62,14 +62,14 @@ function applyMarkToYFragment(
|
|||||||
) {
|
) {
|
||||||
let pos = 0;
|
let pos = 0;
|
||||||
|
|
||||||
const processItem = (item: any): boolean => {
|
const processItem = (item: any, parentNodeName?: string): boolean => {
|
||||||
if (pos >= to) return false;
|
if (pos >= to) return false;
|
||||||
|
|
||||||
if (item instanceof Y.XmlText) {
|
if (item instanceof Y.XmlText) {
|
||||||
const textLength = item.length;
|
const textLength = item.length;
|
||||||
const itemEnd = pos + textLength;
|
const itemEnd = pos + textLength;
|
||||||
|
|
||||||
if (itemEnd > from && pos < to) {
|
if (itemEnd > from && pos < to && parentNodeName !== 'codeBlock') {
|
||||||
const formatFrom = Math.max(0, from - pos);
|
const formatFrom = Math.max(0, from - pos);
|
||||||
const formatTo = Math.min(textLength, to - pos);
|
const formatTo = Math.min(textLength, to - pos);
|
||||||
const formatLength = formatTo - formatFrom;
|
const formatLength = formatTo - formatFrom;
|
||||||
@@ -82,7 +82,7 @@ function applyMarkToYFragment(
|
|||||||
} else if (item instanceof Y.XmlElement) {
|
} else if (item instanceof Y.XmlElement) {
|
||||||
pos++; // Opening tag
|
pos++; // Opening tag
|
||||||
for (let i = 0; i < item.length; i++) {
|
for (let i = 0; i < item.length; i++) {
|
||||||
if (!processItem(item.get(i))) return false;
|
if (!processItem(item.get(i), item.nodeName)) return false;
|
||||||
}
|
}
|
||||||
pos++; // Closing tag
|
pos++; // Closing tag
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
Submodule apps/server/src/ee updated: 211783940c...35c0f3c4f8
@@ -112,7 +112,10 @@ export class EnvironmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getAwsS3ForcePathStyle(): boolean {
|
getAwsS3ForcePathStyle(): boolean {
|
||||||
return this.configService.get<boolean>('AWS_S3_FORCE_PATH_STYLE');
|
const forcePathStyle = this.configService
|
||||||
|
.get<string>('AWS_S3_FORCE_PATH_STYLE', 'false')
|
||||||
|
.toLowerCase();
|
||||||
|
return forcePathStyle === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
getAwsS3Url(): string {
|
getAwsS3Url(): string {
|
||||||
@@ -131,6 +134,17 @@ export class EnvironmentService {
|
|||||||
return this.configService.get<string>('MAIL_FROM_NAME', 'Docmost');
|
return this.configService.get<string>('MAIL_FROM_NAME', 'Docmost');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMailBlockedRecipientDomains(): string[] {
|
||||||
|
const raw = this.configService.get<string>(
|
||||||
|
'MAIL_BLOCKED_RECIPIENT_DOMAINS',
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
return raw
|
||||||
|
.split(',')
|
||||||
|
.map((d) => d.trim().toLowerCase())
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
getSmtpHost(): string {
|
getSmtpHost(): string {
|
||||||
return this.configService.get<string>('SMTP_HOST');
|
return this.configService.get<string>('SMTP_HOST');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { EnvironmentService } from '../environment/environment.service';
|
|||||||
import { InjectQueue } from '@nestjs/bullmq';
|
import { InjectQueue } from '@nestjs/bullmq';
|
||||||
import { QueueName, QueueJob } from '../queue/constants';
|
import { QueueName, QueueJob } from '../queue/constants';
|
||||||
import { Queue } from 'bullmq';
|
import { Queue } from 'bullmq';
|
||||||
import { render } from '@react-email/render';
|
import { render } from 'react-email';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MailService {
|
export class MailService {
|
||||||
@@ -17,6 +17,10 @@ export class MailService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async sendEmail(message: MailMessage): Promise<void> {
|
async sendEmail(message: MailMessage): Promise<void> {
|
||||||
|
if (this.isRecipientBlocked(message.to)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (message.template) {
|
if (message.template) {
|
||||||
// in case this method is used directly. we do not send the tsx template from queue
|
// in case this method is used directly. we do not send the tsx template from queue
|
||||||
message.html = await render(message.template, {
|
message.html = await render(message.template, {
|
||||||
@@ -35,6 +39,10 @@ export class MailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async sendToQueue(message: MailMessage): Promise<void> {
|
async sendToQueue(message: MailMessage): Promise<void> {
|
||||||
|
if (this.isRecipientBlocked(message.to)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (message.template) {
|
if (message.template) {
|
||||||
// transform the React object because it gets lost when sent via the queue
|
// transform the React object because it gets lost when sent via the queue
|
||||||
message.html = await render(message.template, {
|
message.html = await render(message.template, {
|
||||||
@@ -47,4 +55,11 @@ export class MailService {
|
|||||||
}
|
}
|
||||||
await this.emailQueue.add(QueueJob.SEND_EMAIL, message);
|
await this.emailQueue.add(QueueJob.SEND_EMAIL, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isRecipientBlocked(to: string): boolean {
|
||||||
|
const blocked = this.environmentService.getMailBlockedRecipientDomains();
|
||||||
|
if (blocked.length === 0) return false;
|
||||||
|
const domain = to?.split('@')[1]?.toLowerCase();
|
||||||
|
return !!domain && blocked.includes(domain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section, Text } from '@react-email/components';
|
import { Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, paragraph } from '../css/styles';
|
import { content, paragraph } from '../css/styles';
|
||||||
import { EmailButton, MailBody } from '../partials/partials';
|
import { EmailButton, MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section, Text } from '@react-email/components';
|
import { Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, paragraph } from '../css/styles';
|
import { content, paragraph } from '../css/styles';
|
||||||
import { EmailButton, MailBody } from '../partials/partials';
|
import { EmailButton, MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section, Text } from '@react-email/components';
|
import { Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, paragraph } from '../css/styles';
|
import { content, paragraph } from '../css/styles';
|
||||||
import { MailBody } from '../partials/partials';
|
import { MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section, Text } from '@react-email/components';
|
import { Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, paragraph } from '../css/styles';
|
import { content, paragraph } from '../css/styles';
|
||||||
import { EmailButton, MailBody } from '../partials/partials';
|
import { EmailButton, MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section, Text } from '@react-email/components';
|
import { Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, paragraph } from '../css/styles';
|
import { content, paragraph } from '../css/styles';
|
||||||
import { EmailButton, MailBody } from '../partials/partials';
|
import { EmailButton, MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section, Text } from '@react-email/components';
|
import { Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, paragraph } from '../css/styles';
|
import { content, paragraph } from '../css/styles';
|
||||||
import { EmailButton, MailBody } from '../partials/partials';
|
import { EmailButton, MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button, Link, Section, Text } from '@react-email/components';
|
import { Button, Link, Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { button, content, paragraph } from '../css/styles';
|
import { button, content, paragraph } from '../css/styles';
|
||||||
import { MailBody } from '../partials/partials';
|
import { MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section, Text } from '@react-email/components';
|
import { Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, paragraph } from '../css/styles';
|
import { content, paragraph } from '../css/styles';
|
||||||
import { MailBody } from '../partials/partials';
|
import { MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section, Text } from '@react-email/components';
|
import { Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, paragraph } from '../css/styles';
|
import { content, paragraph } from '../css/styles';
|
||||||
import { EmailButton, MailBody } from '../partials/partials';
|
import { EmailButton, MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section, Text } from '@react-email/components';
|
import { Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, paragraph } from '../css/styles';
|
import { content, paragraph } from '../css/styles';
|
||||||
import { EmailButton, MailBody } from '../partials/partials';
|
import { EmailButton, MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Link, Section, Text } from '@react-email/components';
|
import { Link, Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, link, paragraph } from '../css/styles';
|
import { content, link, paragraph } from '../css/styles';
|
||||||
import { getGreetingName, MailBody } from '../partials/partials';
|
import { getGreetingName, MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Link, Section, Text } from '@react-email/components';
|
import { Link, Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, link, paragraph } from '../css/styles';
|
import { content, link, paragraph } from '../css/styles';
|
||||||
import { EmailButton, getGreetingName, MailBody } from '../partials/partials';
|
import { EmailButton, getGreetingName, MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section, Text } from '@react-email/components';
|
import { Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, paragraph } from '../css/styles';
|
import { content, paragraph } from '../css/styles';
|
||||||
import { EmailButton, MailBody } from '../partials/partials';
|
import { EmailButton, MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section, Text } from '@react-email/components';
|
import { Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, paragraph } from '../css/styles';
|
import { content, paragraph } from '../css/styles';
|
||||||
import { EmailButton, MailBody } from '../partials/partials';
|
import { EmailButton, MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section, Text } from '@react-email/components';
|
import { Section, Text } from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { content, paragraph } from '../css/styles';
|
import { content, paragraph } from '../css/styles';
|
||||||
import { EmailButton, MailBody } from '../partials/partials';
|
import { EmailButton, MailBody } from '../partials/partials';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
Row,
|
Row,
|
||||||
Section,
|
Section,
|
||||||
Text,
|
Text,
|
||||||
} from '@react-email/components';
|
} from 'react-email';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
interface MailBodyProps {
|
interface MailBodyProps {
|
||||||
|
|||||||
@@ -50,6 +50,14 @@ async function bootstrap() {
|
|||||||
await app.register(fastifyMultipart);
|
await app.register(fastifyMultipart);
|
||||||
await app.register(fastifyCookie);
|
await app.register(fastifyCookie);
|
||||||
|
|
||||||
|
app
|
||||||
|
.getHttpAdapter()
|
||||||
|
.getInstance()
|
||||||
|
.addHook('onRequest', (request, _reply, done) => {
|
||||||
|
(request.raw as any).ip = request.ip;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
app
|
app
|
||||||
.getHttpAdapter()
|
.getHttpAdapter()
|
||||||
.getInstance()
|
.getInstance()
|
||||||
|
|||||||
Generated
+215
-586
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user