Compare commits
4 Commits
v1.6.0-rc.
...
v1.6.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3ee732a9b | ||
|
|
c3035dbd15 | ||
|
|
7f5b27372f | ||
|
|
b0c081683f |
@@ -78,6 +78,8 @@ NEXT_PRIVATE_SMTP_APIKEY_USER=
|
|||||||
NEXT_PRIVATE_SMTP_APIKEY=
|
NEXT_PRIVATE_SMTP_APIKEY=
|
||||||
# OPTIONAL: Defines whether to force the use of TLS.
|
# OPTIONAL: Defines whether to force the use of TLS.
|
||||||
NEXT_PRIVATE_SMTP_SECURE=
|
NEXT_PRIVATE_SMTP_SECURE=
|
||||||
|
# OPTIONAL: if this is true and NEXT_PRIVATE_SMTP_SECURE is false then TLS is not used even if the server supports STARTTLS extension
|
||||||
|
NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS=
|
||||||
# REQUIRED: Defines the sender name to use for the from address.
|
# REQUIRED: Defines the sender name to use for the from address.
|
||||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||||
# REQUIRED: Defines the email address to use as the from address.
|
# REQUIRED: Defines the email address to use as the from address.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/marketing",
|
"name": "@documenso/marketing",
|
||||||
"version": "1.6.0-rc.1",
|
"version": "1.6.0-rc.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/web",
|
"name": "@documenso/web",
|
||||||
"version": "1.6.0-rc.1",
|
"version": "1.6.0-rc.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ export const EditDocumentForm = ({
|
|||||||
teamId: team?.id,
|
teamId: team?.id,
|
||||||
data: {
|
data: {
|
||||||
title: data.title,
|
title: data.title,
|
||||||
|
externalId: data.externalId || null,
|
||||||
globalAccessAuth: data.globalAccessAuth ?? null,
|
globalAccessAuth: data.globalAccessAuth ?? null,
|
||||||
globalActionAuth: data.globalActionAuth ?? null,
|
globalActionAuth: data.globalActionAuth ?? null,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ export const EditTemplateForm = ({
|
|||||||
teamId: team?.id,
|
teamId: team?.id,
|
||||||
data: {
|
data: {
|
||||||
title: data.title,
|
title: data.title,
|
||||||
|
externalId: data.externalId || null,
|
||||||
globalAccessAuth: data.globalAccessAuth ?? null,
|
globalAccessAuth: data.globalAccessAuth ?? null,
|
||||||
globalActionAuth: data.globalActionAuth ?? null,
|
globalActionAuth: data.globalActionAuth ?? null,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -271,6 +271,23 @@ export const DocumentHistorySheet = ({
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
|
.with(
|
||||||
|
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_EXTERNAL_ID_UPDATED },
|
||||||
|
({ data }) => (
|
||||||
|
<DocumentHistorySheetChanges
|
||||||
|
values={[
|
||||||
|
{
|
||||||
|
key: 'Old',
|
||||||
|
value: data.from,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'New',
|
||||||
|
value: data.to,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
)
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED }, ({ data }) => (
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED }, ({ data }) => (
|
||||||
<DocumentHistorySheetChanges
|
<DocumentHistorySheetChanges
|
||||||
values={[
|
values={[
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ Here's a markdown table documenting all the provided environment variables:
|
|||||||
| `NEXT_PRIVATE_SMTP_APIKEY_USER` | The API key user for the SMTP server for the `smtp-api` transport. |
|
| `NEXT_PRIVATE_SMTP_APIKEY_USER` | The API key user for the SMTP server for the `smtp-api` transport. |
|
||||||
| `NEXT_PRIVATE_SMTP_APIKEY` | The API key for the SMTP server for the `smtp-api` transport. |
|
| `NEXT_PRIVATE_SMTP_APIKEY` | The API key for the SMTP server for the `smtp-api` transport. |
|
||||||
| `NEXT_PRIVATE_SMTP_SECURE` | Whether to force the use of TLS for the SMTP server for SMTP transports. |
|
| `NEXT_PRIVATE_SMTP_SECURE` | Whether to force the use of TLS for the SMTP server for SMTP transports. |
|
||||||
|
| `NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS` | If true, then no TLS will be used (even if STARTTLS is supported) |
|
||||||
| `NEXT_PRIVATE_SMTP_FROM_ADDRESS` | The email address for the "from" address. |
|
| `NEXT_PRIVATE_SMTP_FROM_ADDRESS` | The email address for the "from" address. |
|
||||||
| `NEXT_PRIVATE_SMTP_FROM_NAME` | The sender name for the "from" address. |
|
| `NEXT_PRIVATE_SMTP_FROM_NAME` | The sender name for the "from" address. |
|
||||||
| `NEXT_PRIVATE_RESEND_API_KEY` | The API key for Resend.com for the `resend` transport. |
|
| `NEXT_PRIVATE_RESEND_API_KEY` | The API key for Resend.com for the `resend` transport. |
|
||||||
|
|||||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/root",
|
"name": "@documenso/root",
|
||||||
"version": "1.6.0-rc.1",
|
"version": "1.6.0-rc.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@documenso/root",
|
"name": "@documenso/root",
|
||||||
"version": "1.6.0-rc.1",
|
"version": "1.6.0-rc.2",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"packages/*"
|
"packages/*"
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
},
|
},
|
||||||
"apps/marketing": {
|
"apps/marketing": {
|
||||||
"name": "@documenso/marketing",
|
"name": "@documenso/marketing",
|
||||||
"version": "1.6.0-rc.1",
|
"version": "1.6.0-rc.2",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@documenso/assets": "*",
|
"@documenso/assets": "*",
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
},
|
},
|
||||||
"apps/web": {
|
"apps/web": {
|
||||||
"name": "@documenso/web",
|
"name": "@documenso/web",
|
||||||
"version": "1.6.0-rc.1",
|
"version": "1.6.0-rc.2",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@documenso/api": "*",
|
"@documenso/api": "*",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.6.0-rc.1",
|
"version": "1.6.0-rc.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
"build:web": "turbo run build --filter=@documenso/web",
|
"build:web": "turbo run build --filter=@documenso/web",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
ZGetDocumentsQuerySchema,
|
ZGetDocumentsQuerySchema,
|
||||||
ZGetTemplatesQuerySchema,
|
ZGetTemplatesQuerySchema,
|
||||||
ZNoBodyMutationSchema,
|
ZNoBodyMutationSchema,
|
||||||
|
ZResendDocumentForSigningMutationSchema,
|
||||||
ZSendDocumentForSigningMutationSchema,
|
ZSendDocumentForSigningMutationSchema,
|
||||||
ZSuccessfulDeleteTemplateResponseSchema,
|
ZSuccessfulDeleteTemplateResponseSchema,
|
||||||
ZSuccessfulDocumentResponseSchema,
|
ZSuccessfulDocumentResponseSchema,
|
||||||
@@ -25,6 +26,7 @@ import {
|
|||||||
ZSuccessfulGetTemplateResponseSchema,
|
ZSuccessfulGetTemplateResponseSchema,
|
||||||
ZSuccessfulGetTemplatesResponseSchema,
|
ZSuccessfulGetTemplatesResponseSchema,
|
||||||
ZSuccessfulRecipientResponseSchema,
|
ZSuccessfulRecipientResponseSchema,
|
||||||
|
ZSuccessfulResendDocumentResponseSchema,
|
||||||
ZSuccessfulResponseSchema,
|
ZSuccessfulResponseSchema,
|
||||||
ZSuccessfulSigningResponseSchema,
|
ZSuccessfulSigningResponseSchema,
|
||||||
ZUnsuccessfulResponseSchema,
|
ZUnsuccessfulResponseSchema,
|
||||||
@@ -161,6 +163,20 @@ export const ApiContractV1 = c.router(
|
|||||||
summary: 'Send a document for signing',
|
summary: 'Send a document for signing',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
resendDocument: {
|
||||||
|
method: 'POST',
|
||||||
|
path: '/api/v1/documents/:id/resend',
|
||||||
|
body: ZResendDocumentForSigningMutationSchema,
|
||||||
|
responses: {
|
||||||
|
200: ZSuccessfulResendDocumentResponseSchema,
|
||||||
|
400: ZUnsuccessfulResponseSchema,
|
||||||
|
401: ZUnsuccessfulResponseSchema,
|
||||||
|
404: ZUnsuccessfulResponseSchema,
|
||||||
|
500: ZUnsuccessfulResponseSchema,
|
||||||
|
},
|
||||||
|
summary: 'Re-send a document for signing',
|
||||||
|
},
|
||||||
|
|
||||||
deleteDocument: {
|
deleteDocument: {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
path: '/api/v1/documents/:id',
|
path: '/api/v1/documents/:id',
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { createDocument } from '@documenso/lib/server-only/document/create-docum
|
|||||||
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
|
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
|
||||||
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
|
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
|
||||||
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
|
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
|
||||||
|
import { resendDocument } from '@documenso/lib/server-only/document/resend-document';
|
||||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||||
import { updateDocument } from '@documenso/lib/server-only/document/update-document';
|
import { updateDocument } from '@documenso/lib/server-only/document/update-document';
|
||||||
import { createField } from '@documenso/lib/server-only/field/create-field';
|
import { createField } from '@documenso/lib/server-only/field/create-field';
|
||||||
@@ -232,6 +233,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
|||||||
|
|
||||||
const document = await createDocument({
|
const document = await createDocument({
|
||||||
title: body.title,
|
title: body.title,
|
||||||
|
externalId: body.externalId || null,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
teamId: team?.id,
|
teamId: team?.id,
|
||||||
formValues: body.formValues,
|
formValues: body.formValues,
|
||||||
@@ -397,6 +399,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
|||||||
teamId: team?.id,
|
teamId: team?.id,
|
||||||
data: {
|
data: {
|
||||||
title: fileName,
|
title: fileName,
|
||||||
|
externalId: body.externalId || null,
|
||||||
formValues: body.formValues,
|
formValues: body.formValues,
|
||||||
documentData: {
|
documentData: {
|
||||||
connect: {
|
connect: {
|
||||||
@@ -453,6 +456,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
|||||||
try {
|
try {
|
||||||
document = await createDocumentFromTemplate({
|
document = await createDocumentFromTemplate({
|
||||||
templateId,
|
templateId,
|
||||||
|
externalId: body.externalId || null,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
teamId: team?.id,
|
teamId: team?.id,
|
||||||
recipients: body.recipients,
|
recipients: body.recipients,
|
||||||
@@ -600,6 +604,35 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
resendDocument: authenticatedMiddleware(async (args, user, team) => {
|
||||||
|
const { id: documentId } = args.params;
|
||||||
|
const { recipients } = args.body;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await resendDocument({
|
||||||
|
userId: user.id,
|
||||||
|
documentId: Number(documentId),
|
||||||
|
recipients,
|
||||||
|
teamId: team?.id,
|
||||||
|
requestMetadata: extractNextApiRequestMetadata(args.req),
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
message: 'Document resend successfully initiated',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
body: {
|
||||||
|
message: 'An error has occured while resending the document',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
createRecipient: authenticatedMiddleware(async (args, user, team) => {
|
createRecipient: authenticatedMiddleware(async (args, user, team) => {
|
||||||
const { id: documentId } = args.params;
|
const { id: documentId } = args.params;
|
||||||
const { name, email, role } = args.body;
|
const { name, email, role } = args.body;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export type TDeleteDocumentMutationSchema = typeof ZDeleteDocumentMutationSchema
|
|||||||
|
|
||||||
export const ZSuccessfulDocumentResponseSchema = z.object({
|
export const ZSuccessfulDocumentResponseSchema = z.object({
|
||||||
id: z.number(),
|
id: z.number(),
|
||||||
|
externalId: z.string().nullish(),
|
||||||
userId: z.number(),
|
userId: z.number(),
|
||||||
teamId: z.number().nullish(),
|
teamId: z.number().nullish(),
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
@@ -57,6 +58,20 @@ export const ZSendDocumentForSigningMutationSchema = z
|
|||||||
|
|
||||||
export type TSendDocumentForSigningMutationSchema = typeof ZSendDocumentForSigningMutationSchema;
|
export type TSendDocumentForSigningMutationSchema = typeof ZSendDocumentForSigningMutationSchema;
|
||||||
|
|
||||||
|
export const ZResendDocumentForSigningMutationSchema = z.object({
|
||||||
|
recipients: z.array(z.number()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TResendDocumentForSigningMutationSchema = z.infer<
|
||||||
|
typeof ZResendDocumentForSigningMutationSchema
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const ZSuccessfulResendDocumentResponseSchema = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TResendDocumentResponseSchema = z.infer<typeof ZSuccessfulResendDocumentResponseSchema>;
|
||||||
|
|
||||||
export const ZUploadDocumentSuccessfulSchema = z.object({
|
export const ZUploadDocumentSuccessfulSchema = z.object({
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
key: z.string(),
|
key: z.string(),
|
||||||
@@ -70,6 +85,7 @@ export type TUploadDocumentSuccessfulSchema = z.infer<typeof ZUploadDocumentSucc
|
|||||||
|
|
||||||
export const ZCreateDocumentMutationSchema = z.object({
|
export const ZCreateDocumentMutationSchema = z.object({
|
||||||
title: z.string().min(1),
|
title: z.string().min(1),
|
||||||
|
externalId: z.string().nullish(),
|
||||||
recipients: z.array(
|
recipients: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
@@ -94,6 +110,7 @@ export type TCreateDocumentMutationSchema = z.infer<typeof ZCreateDocumentMutati
|
|||||||
export const ZCreateDocumentMutationResponseSchema = z.object({
|
export const ZCreateDocumentMutationResponseSchema = z.object({
|
||||||
uploadUrl: z.string().min(1),
|
uploadUrl: z.string().min(1),
|
||||||
documentId: z.number(),
|
documentId: z.number(),
|
||||||
|
externalId: z.string().nullish(),
|
||||||
recipients: z.array(
|
recipients: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
recipientId: z.number(),
|
recipientId: z.number(),
|
||||||
@@ -113,6 +130,7 @@ export type TCreateDocumentMutationResponseSchema = z.infer<
|
|||||||
|
|
||||||
export const ZCreateDocumentFromTemplateMutationSchema = z.object({
|
export const ZCreateDocumentFromTemplateMutationSchema = z.object({
|
||||||
title: z.string().min(1),
|
title: z.string().min(1),
|
||||||
|
externalId: z.string().nullish(),
|
||||||
recipients: z.array(
|
recipients: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
@@ -139,6 +157,7 @@ export type TCreateDocumentFromTemplateMutationSchema = z.infer<
|
|||||||
|
|
||||||
export const ZCreateDocumentFromTemplateMutationResponseSchema = z.object({
|
export const ZCreateDocumentFromTemplateMutationResponseSchema = z.object({
|
||||||
documentId: z.number(),
|
documentId: z.number(),
|
||||||
|
externalId: z.string().nullish(),
|
||||||
recipients: z.array(
|
recipients: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
recipientId: z.number(),
|
recipientId: z.number(),
|
||||||
@@ -158,6 +177,7 @@ export type TCreateDocumentFromTemplateMutationResponseSchema = z.infer<
|
|||||||
|
|
||||||
export const ZGenerateDocumentFromTemplateMutationSchema = z.object({
|
export const ZGenerateDocumentFromTemplateMutationSchema = z.object({
|
||||||
title: z.string().optional(),
|
title: z.string().optional(),
|
||||||
|
externalId: z.string().nullish(),
|
||||||
recipients: z
|
recipients: z
|
||||||
.array(
|
.array(
|
||||||
z.object({
|
z.object({
|
||||||
@@ -194,6 +214,7 @@ export type TGenerateDocumentFromTemplateMutationSchema = z.infer<
|
|||||||
|
|
||||||
export const ZGenerateDocumentFromTemplateMutationResponseSchema = z.object({
|
export const ZGenerateDocumentFromTemplateMutationResponseSchema = z.object({
|
||||||
documentId: z.number(),
|
documentId: z.number(),
|
||||||
|
externalId: z.string().nullish(),
|
||||||
recipients: z.array(
|
recipients: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
recipientId: z.number(),
|
recipientId: z.number(),
|
||||||
@@ -332,6 +353,7 @@ export const ZTemplateMetaSchema = z.object({
|
|||||||
|
|
||||||
export const ZTemplateSchema = z.object({
|
export const ZTemplateSchema = z.object({
|
||||||
id: z.number(),
|
id: z.number(),
|
||||||
|
externalId: z.string().nullish(),
|
||||||
type: z.nativeEnum(TemplateType),
|
type: z.nativeEnum(TemplateType),
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
userId: z.number(),
|
userId: z.number(),
|
||||||
|
|||||||
@@ -46,10 +46,13 @@ const getTransport = () => {
|
|||||||
host: process.env.NEXT_PRIVATE_SMTP_HOST ?? 'localhost:2500',
|
host: process.env.NEXT_PRIVATE_SMTP_HOST ?? 'localhost:2500',
|
||||||
port: Number(process.env.NEXT_PRIVATE_SMTP_PORT) || 587,
|
port: Number(process.env.NEXT_PRIVATE_SMTP_PORT) || 587,
|
||||||
secure: process.env.NEXT_PRIVATE_SMTP_SECURE === 'true',
|
secure: process.env.NEXT_PRIVATE_SMTP_SECURE === 'true',
|
||||||
auth: {
|
ignoreTLS: process.env.NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS === 'true',
|
||||||
user: process.env.NEXT_PRIVATE_SMTP_USERNAME ?? '',
|
auth: process.env.NEXT_PRIVATE_SMTP_USERNAME
|
||||||
|
? {
|
||||||
|
user: process.env.NEXT_PRIVATE_SMTP_USERNAME,
|
||||||
pass: process.env.NEXT_PRIVATE_SMTP_PASSWORD ?? '',
|
pass: process.env.NEXT_PRIVATE_SMTP_PASSWORD ?? '',
|
||||||
},
|
}
|
||||||
|
: undefined,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export const sendConfirmationEmail = async ({ userId }: SendConfirmationEmailPro
|
|||||||
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
|
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
|
||||||
const confirmationLink = `${assetBaseUrl}/verify-email/${verificationToken.token}`;
|
const confirmationLink = `${assetBaseUrl}/verify-email/${verificationToken.token}`;
|
||||||
const senderName = NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso';
|
const senderName = NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso';
|
||||||
const senderAdress = NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com';
|
const senderAddress = NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com';
|
||||||
|
|
||||||
const confirmationTemplate = createElement(ConfirmEmailTemplate, {
|
const confirmationTemplate = createElement(ConfirmEmailTemplate, {
|
||||||
assetBaseUrl,
|
assetBaseUrl,
|
||||||
@@ -52,7 +52,7 @@ export const sendConfirmationEmail = async ({ userId }: SendConfirmationEmailPro
|
|||||||
},
|
},
|
||||||
from: {
|
from: {
|
||||||
name: senderName,
|
name: senderName,
|
||||||
address: senderAdress,
|
address: senderAddress,
|
||||||
},
|
},
|
||||||
subject: 'Please confirm your email',
|
subject: 'Please confirm your email',
|
||||||
html: render(confirmationTemplate),
|
html: render(confirmationTemplate),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
|||||||
|
|
||||||
export type CreateDocumentOptions = {
|
export type CreateDocumentOptions = {
|
||||||
title: string;
|
title: string;
|
||||||
|
externalId?: string | null;
|
||||||
userId: number;
|
userId: number;
|
||||||
teamId?: number;
|
teamId?: number;
|
||||||
documentDataId: string;
|
documentDataId: string;
|
||||||
@@ -21,6 +22,7 @@ export type CreateDocumentOptions = {
|
|||||||
export const createDocument = async ({
|
export const createDocument = async ({
|
||||||
userId,
|
userId,
|
||||||
title,
|
title,
|
||||||
|
externalId,
|
||||||
documentDataId,
|
documentDataId,
|
||||||
teamId,
|
teamId,
|
||||||
formValues,
|
formValues,
|
||||||
@@ -50,6 +52,7 @@ export const createDocument = async ({
|
|||||||
const document = await tx.document.create({
|
const document = await tx.document.create({
|
||||||
data: {
|
data: {
|
||||||
title,
|
title,
|
||||||
|
externalId,
|
||||||
documentDataId,
|
documentDataId,
|
||||||
userId,
|
userId,
|
||||||
teamId,
|
teamId,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export type UpdateDocumentSettingsOptions = {
|
|||||||
documentId: number;
|
documentId: number;
|
||||||
data: {
|
data: {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
externalId?: string | null;
|
||||||
globalAccessAuth?: TDocumentAccessAuthTypes | null;
|
globalAccessAuth?: TDocumentAccessAuthTypes | null;
|
||||||
globalActionAuth?: TDocumentActionAuthTypes | null;
|
globalActionAuth?: TDocumentActionAuthTypes | null;
|
||||||
};
|
};
|
||||||
@@ -91,6 +92,7 @@ export const updateDocumentSettings = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isTitleSame = data.title === document.title;
|
const isTitleSame = data.title === document.title;
|
||||||
|
const isExternalIdSame = data.externalId === document.externalId;
|
||||||
const isGlobalAccessSame = documentGlobalAccessAuth === newGlobalAccessAuth;
|
const isGlobalAccessSame = documentGlobalAccessAuth === newGlobalAccessAuth;
|
||||||
const isGlobalActionSame = documentGlobalActionAuth === newGlobalActionAuth;
|
const isGlobalActionSame = documentGlobalActionAuth === newGlobalActionAuth;
|
||||||
|
|
||||||
@@ -118,6 +120,21 @@ export const updateDocumentSettings = async ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isExternalIdSame) {
|
||||||
|
auditLogs.push(
|
||||||
|
createDocumentAuditLogData({
|
||||||
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_EXTERNAL_ID_UPDATED,
|
||||||
|
documentId,
|
||||||
|
user,
|
||||||
|
requestMetadata,
|
||||||
|
data: {
|
||||||
|
from: document.externalId,
|
||||||
|
to: data.externalId || '',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isGlobalAccessSame) {
|
if (!isGlobalAccessSame) {
|
||||||
auditLogs.push(
|
auditLogs.push(
|
||||||
createDocumentAuditLogData({
|
createDocumentAuditLogData({
|
||||||
@@ -165,6 +182,7 @@ export const updateDocumentSettings = async ({
|
|||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
title: data.title,
|
title: data.title,
|
||||||
|
externalId: data.externalId || null,
|
||||||
authOptions,
|
authOptions,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export type CreateDocumentFromTemplateResponse = Awaited<
|
|||||||
|
|
||||||
export type CreateDocumentFromTemplateOptions = {
|
export type CreateDocumentFromTemplateOptions = {
|
||||||
templateId: number;
|
templateId: number;
|
||||||
|
externalId?: string | null;
|
||||||
userId: number;
|
userId: number;
|
||||||
teamId?: number;
|
teamId?: number;
|
||||||
recipients: {
|
recipients: {
|
||||||
@@ -58,6 +59,7 @@ export type CreateDocumentFromTemplateOptions = {
|
|||||||
|
|
||||||
export const createDocumentFromTemplate = async ({
|
export const createDocumentFromTemplate = async ({
|
||||||
templateId,
|
templateId,
|
||||||
|
externalId,
|
||||||
userId,
|
userId,
|
||||||
teamId,
|
teamId,
|
||||||
recipients,
|
recipients,
|
||||||
@@ -147,6 +149,7 @@ export const createDocumentFromTemplate = async ({
|
|||||||
const document = await tx.document.create({
|
const document = await tx.document.create({
|
||||||
data: {
|
data: {
|
||||||
source: DocumentSource.TEMPLATE,
|
source: DocumentSource.TEMPLATE,
|
||||||
|
externalId,
|
||||||
templateId: template.id,
|
templateId: template.id,
|
||||||
userId,
|
userId,
|
||||||
teamId: template.teamId,
|
teamId: template.teamId,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export type UpdateTemplateSettingsOptions = {
|
|||||||
templateId: number;
|
templateId: number;
|
||||||
data: {
|
data: {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
externalId?: string | null;
|
||||||
globalAccessAuth?: TDocumentAccessAuthTypes | null;
|
globalAccessAuth?: TDocumentAccessAuthTypes | null;
|
||||||
globalActionAuth?: TDocumentActionAuthTypes | null;
|
globalActionAuth?: TDocumentActionAuthTypes | null;
|
||||||
publicTitle?: string;
|
publicTitle?: string;
|
||||||
@@ -99,6 +100,7 @@ export const updateTemplateSettings = async ({
|
|||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
title: data.title,
|
title: data.title,
|
||||||
|
externalId: data.externalId || null,
|
||||||
type: data.type,
|
type: data.type,
|
||||||
publicDescription: data.publicDescription,
|
publicDescription: data.publicDescription,
|
||||||
publicTitle: data.publicTitle,
|
publicTitle: data.publicTitle,
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export const ZDocumentAuditLogTypeSchema = z.enum([
|
|||||||
'DOCUMENT_RECIPIENT_COMPLETED', // When a recipient completes all their required tasks for the document.
|
'DOCUMENT_RECIPIENT_COMPLETED', // When a recipient completes all their required tasks for the document.
|
||||||
'DOCUMENT_SENT', // When the document transitions from DRAFT to PENDING.
|
'DOCUMENT_SENT', // When the document transitions from DRAFT to PENDING.
|
||||||
'DOCUMENT_TITLE_UPDATED', // When the document title is updated.
|
'DOCUMENT_TITLE_UPDATED', // When the document title is updated.
|
||||||
|
'DOCUMENT_EXTERNAL_ID_UPDATED', // When the document external ID is updated.
|
||||||
'DOCUMENT_MOVED_TO_TEAM', // When the document is moved to a team.
|
'DOCUMENT_MOVED_TO_TEAM', // When the document is moved to a team.
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -355,6 +356,17 @@ export const ZDocumentAuditLogEventDocumentTitleUpdatedSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event: Document external ID updated.
|
||||||
|
*/
|
||||||
|
export const ZDocumentAuditLogEventDocumentExternalIdUpdatedSchema = z.object({
|
||||||
|
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_EXTERNAL_ID_UPDATED),
|
||||||
|
data: z.object({
|
||||||
|
from: z.string().nullish(),
|
||||||
|
to: z.string().nullish(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event: Field created.
|
* Event: Field created.
|
||||||
*/
|
*/
|
||||||
@@ -450,6 +462,7 @@ export const ZDocumentAuditLogSchema = ZDocumentAuditLogBaseSchema.and(
|
|||||||
ZDocumentAuditLogEventDocumentRecipientCompleteSchema,
|
ZDocumentAuditLogEventDocumentRecipientCompleteSchema,
|
||||||
ZDocumentAuditLogEventDocumentSentSchema,
|
ZDocumentAuditLogEventDocumentSentSchema,
|
||||||
ZDocumentAuditLogEventDocumentTitleUpdatedSchema,
|
ZDocumentAuditLogEventDocumentTitleUpdatedSchema,
|
||||||
|
ZDocumentAuditLogEventDocumentExternalIdUpdatedSchema,
|
||||||
ZDocumentAuditLogEventFieldCreatedSchema,
|
ZDocumentAuditLogEventFieldCreatedSchema,
|
||||||
ZDocumentAuditLogEventFieldRemovedSchema,
|
ZDocumentAuditLogEventFieldRemovedSchema,
|
||||||
ZDocumentAuditLogEventFieldUpdatedSchema,
|
ZDocumentAuditLogEventFieldUpdatedSchema,
|
||||||
|
|||||||
@@ -332,6 +332,10 @@ export const formatDocumentAuditLogAction = (auditLog: TDocumentAuditLog, userId
|
|||||||
anonymous: 'Document title updated',
|
anonymous: 'Document title updated',
|
||||||
identified: 'updated the document title',
|
identified: 'updated the document title',
|
||||||
}))
|
}))
|
||||||
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_EXTERNAL_ID_UPDATED }, () => ({
|
||||||
|
anonymous: 'Document external ID updated',
|
||||||
|
identified: 'updated the document external ID',
|
||||||
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT }, () => ({
|
||||||
anonymous: 'Document sent',
|
anonymous: 'Document sent',
|
||||||
identified: 'sent the document',
|
identified: 'sent the document',
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Document" ADD COLUMN "externalId" TEXT;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Template" ADD COLUMN "externalId" TEXT;
|
||||||
@@ -283,6 +283,7 @@ enum DocumentSource {
|
|||||||
|
|
||||||
model Document {
|
model Document {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
|
externalId String?
|
||||||
userId Int
|
userId Int
|
||||||
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
authOptions Json?
|
authOptions Json?
|
||||||
@@ -589,6 +590,7 @@ model TemplateMeta {
|
|||||||
|
|
||||||
model Template {
|
model Template {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
|
externalId String?
|
||||||
type TemplateType @default(PRIVATE)
|
type TemplateType @default(PRIVATE)
|
||||||
title String
|
title String
|
||||||
userId Int
|
userId Int
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export const ZSetSettingsForDocumentMutationSchema = z.object({
|
|||||||
teamId: z.number().min(1).optional(),
|
teamId: z.number().min(1).optional(),
|
||||||
data: z.object({
|
data: z.object({
|
||||||
title: z.string().min(1).optional(),
|
title: z.string().min(1).optional(),
|
||||||
|
externalId: z.string().nullish(),
|
||||||
globalAccessAuth: ZDocumentAccessAuthTypesSchema.nullable().optional(),
|
globalAccessAuth: ZDocumentAccessAuthTypesSchema.nullable().optional(),
|
||||||
globalActionAuth: ZDocumentActionAuthTypesSchema.nullable().optional(),
|
globalActionAuth: ZDocumentActionAuthTypesSchema.nullable().optional(),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export const ZUpdateTemplateSettingsMutationSchema = z.object({
|
|||||||
teamId: z.number().min(1).optional(),
|
teamId: z.number().min(1).optional(),
|
||||||
data: z.object({
|
data: z.object({
|
||||||
title: z.string().min(1).optional(),
|
title: z.string().min(1).optional(),
|
||||||
|
externalId: z.string().nullish(),
|
||||||
globalAccessAuth: ZDocumentAccessAuthTypesSchema.nullable().optional(),
|
globalAccessAuth: ZDocumentAccessAuthTypesSchema.nullable().optional(),
|
||||||
globalActionAuth: ZDocumentActionAuthTypesSchema.nullable().optional(),
|
globalActionAuth: ZDocumentActionAuthTypesSchema.nullable().optional(),
|
||||||
publicTitle: z.string().trim().min(1).max(MAX_TEMPLATE_PUBLIC_TITLE_LENGTH).optional(),
|
publicTitle: z.string().trim().min(1).max(MAX_TEMPLATE_PUBLIC_TITLE_LENGTH).optional(),
|
||||||
|
|||||||
1
packages/tsconfig/process-env.d.ts
vendored
1
packages/tsconfig/process-env.d.ts
vendored
@@ -59,6 +59,7 @@ declare namespace NodeJS {
|
|||||||
NEXT_PRIVATE_SMTP_APIKEY?: string;
|
NEXT_PRIVATE_SMTP_APIKEY?: string;
|
||||||
|
|
||||||
NEXT_PRIVATE_SMTP_SECURE?: string;
|
NEXT_PRIVATE_SMTP_SECURE?: string;
|
||||||
|
NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS?: string;
|
||||||
|
|
||||||
NEXT_PRIVATE_SMTP_FROM_NAME?: string;
|
NEXT_PRIVATE_SMTP_FROM_NAME?: string;
|
||||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS?: string;
|
NEXT_PRIVATE_SMTP_FROM_ADDRESS?: string;
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export const AddSettingsFormPartial = ({
|
|||||||
resolver: zodResolver(ZAddSettingsFormSchema),
|
resolver: zodResolver(ZAddSettingsFormSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
title: document.title,
|
title: document.title,
|
||||||
|
externalId: document.externalId || '',
|
||||||
globalAccessAuth: documentAuthOption?.globalAccessAuth || undefined,
|
globalAccessAuth: documentAuthOption?.globalAccessAuth || undefined,
|
||||||
globalActionAuth: documentAuthOption?.globalActionAuth || undefined,
|
globalActionAuth: documentAuthOption?.globalActionAuth || undefined,
|
||||||
meta: {
|
meta: {
|
||||||
@@ -183,6 +184,34 @@ export const AddSettingsFormPartial = ({
|
|||||||
|
|
||||||
<AccordionContent className="text-muted-foreground -mx-1 px-1 pt-2 text-sm leading-relaxed">
|
<AccordionContent className="text-muted-foreground -mx-1 px-1 pt-2 text-sm leading-relaxed">
|
||||||
<div className="flex flex-col space-y-6 ">
|
<div className="flex flex-col space-y-6 ">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="externalId"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel className="flex flex-row items-center">
|
||||||
|
External ID{' '}
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="mx-2 h-4 w-4" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
|
||||||
|
<TooltipContent className="text-muted-foreground max-w-xs">
|
||||||
|
Add an external ID to the document. This can be used to identify the
|
||||||
|
document in external systems.
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
|
<FormControl>
|
||||||
|
<Input className="bg-background" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="meta.dateFormat"
|
name="meta.dateFormat"
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export const ZMapNegativeOneToUndefinedSchema = z
|
|||||||
|
|
||||||
export const ZAddSettingsFormSchema = z.object({
|
export const ZAddSettingsFormSchema = z.object({
|
||||||
title: z.string().trim().min(1, { message: "Title can't be empty" }),
|
title: z.string().trim().min(1, { message: "Title can't be empty" }),
|
||||||
|
externalId: z.string().optional(),
|
||||||
globalAccessAuth: ZMapNegativeOneToUndefinedSchema.pipe(
|
globalAccessAuth: ZMapNegativeOneToUndefinedSchema.pipe(
|
||||||
ZDocumentAccessAuthTypesSchema.optional(),
|
ZDocumentAccessAuthTypesSchema.optional(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export const AddTemplateSettingsFormPartial = ({
|
|||||||
resolver: zodResolver(ZAddTemplateSettingsFormSchema),
|
resolver: zodResolver(ZAddTemplateSettingsFormSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
title: template.title,
|
title: template.title,
|
||||||
|
externalId: template.externalId || undefined,
|
||||||
globalAccessAuth: documentAuthOption?.globalAccessAuth || undefined,
|
globalAccessAuth: documentAuthOption?.globalAccessAuth || undefined,
|
||||||
globalActionAuth: documentAuthOption?.globalActionAuth || undefined,
|
globalActionAuth: documentAuthOption?.globalActionAuth || undefined,
|
||||||
meta: {
|
meta: {
|
||||||
@@ -223,6 +224,34 @@ export const AddTemplateSettingsFormPartial = ({
|
|||||||
|
|
||||||
<AccordionContent className="text-muted-foreground -mx-1 px-1 pt-4 text-sm leading-relaxed">
|
<AccordionContent className="text-muted-foreground -mx-1 px-1 pt-4 text-sm leading-relaxed">
|
||||||
<div className="flex flex-col space-y-6">
|
<div className="flex flex-col space-y-6">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="externalId"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel className="flex flex-row items-center">
|
||||||
|
External ID{' '}
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="mx-2 h-4 w-4" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
|
||||||
|
<TooltipContent className="text-muted-foreground max-w-xs">
|
||||||
|
Add an external ID to the template. This can be used to identify in
|
||||||
|
external systems.
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
|
<FormControl>
|
||||||
|
<Input className="bg-background" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="meta.dateFormat"
|
name="meta.dateFormat"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { ZMapNegativeOneToUndefinedSchema } from '../document-flow/add-settings.
|
|||||||
|
|
||||||
export const ZAddTemplateSettingsFormSchema = z.object({
|
export const ZAddTemplateSettingsFormSchema = z.object({
|
||||||
title: z.string().trim().min(1, { message: "Title can't be empty" }),
|
title: z.string().trim().min(1, { message: "Title can't be empty" }),
|
||||||
|
externalId: z.string().optional(),
|
||||||
globalAccessAuth: ZMapNegativeOneToUndefinedSchema.pipe(
|
globalAccessAuth: ZMapNegativeOneToUndefinedSchema.pipe(
|
||||||
ZDocumentAccessAuthTypesSchema.optional(),
|
ZDocumentAccessAuthTypesSchema.optional(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -12,9 +12,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"prebuild": {
|
"prebuild": {
|
||||||
"cache": false,
|
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"^prebuild"
|
"^prebuild"
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
".next/**",
|
||||||
|
"!.next/cache/**"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
@@ -109,6 +112,7 @@
|
|||||||
"NEXT_PRIVATE_SMTP_APIKEY_USER",
|
"NEXT_PRIVATE_SMTP_APIKEY_USER",
|
||||||
"NEXT_PRIVATE_SMTP_APIKEY",
|
"NEXT_PRIVATE_SMTP_APIKEY",
|
||||||
"NEXT_PRIVATE_SMTP_SECURE",
|
"NEXT_PRIVATE_SMTP_SECURE",
|
||||||
|
"NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS",
|
||||||
"NEXT_PRIVATE_SMTP_FROM_NAME",
|
"NEXT_PRIVATE_SMTP_FROM_NAME",
|
||||||
"NEXT_PRIVATE_SMTP_FROM_ADDRESS",
|
"NEXT_PRIVATE_SMTP_FROM_ADDRESS",
|
||||||
"NEXT_PRIVATE_STRIPE_API_KEY",
|
"NEXT_PRIVATE_STRIPE_API_KEY",
|
||||||
|
|||||||
Reference in New Issue
Block a user