Improved DX for contact update, subscribe, and unsubscribe
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "plunk",
|
"name": "plunk",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "agpl-3.0",
|
"license": "agpl-3.0",
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
|
|||||||
@@ -101,16 +101,16 @@ export class Contacts {
|
|||||||
throw new NotFound("project");
|
throw new NotFound("project");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id } = UtilitySchemas.id.parse(req.body);
|
const { id, email } = ContactSchemas.manage.parse(req.body);
|
||||||
|
|
||||||
const contact = await ContactService.id(id);
|
const contact = id ? await ContactService.id(id) : await ContactService.email(project.id, email as string);
|
||||||
|
|
||||||
if (!contact || contact.projectId !== project.id) {
|
if (!contact || contact.projectId !== project.id) {
|
||||||
throw new NotFound("contact");
|
throw new NotFound("contact");
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.contact.update({
|
await prisma.contact.update({
|
||||||
where: { id },
|
where: { id: contact.id },
|
||||||
data: { subscribed: false },
|
data: { subscribed: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -152,16 +152,16 @@ export class Contacts {
|
|||||||
throw new NotFound("project");
|
throw new NotFound("project");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id } = UtilitySchemas.id.parse(req.body);
|
const { id, email } = ContactSchemas.manage.parse(req.body);
|
||||||
|
|
||||||
const contact = await ContactService.id(id);
|
const contact = id ? await ContactService.id(id) : await ContactService.email(project.id, email as string);
|
||||||
|
|
||||||
if (!contact || contact.projectId !== project.id) {
|
if (!contact || contact.projectId !== project.id) {
|
||||||
throw new NotFound("contact");
|
throw new NotFound("contact");
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.contact.update({
|
await prisma.contact.update({
|
||||||
where: { id },
|
where: { id: contact.id },
|
||||||
data: { subscribed: true },
|
data: { subscribed: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -246,26 +246,43 @@ export class Contacts {
|
|||||||
throw new NotFound("project");
|
throw new NotFound("project");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id, email, subscribed, data } = ContactSchemas.update.parse(req.body);
|
const { id, email, subscribed, data } = ContactSchemas.manage.parse(req.body);
|
||||||
|
|
||||||
let contact = await ContactService.id(id);
|
const contact = id ? await ContactService.id(id) : await ContactService.email(project.id, email as string);
|
||||||
|
|
||||||
if (!contact || contact.projectId !== project.id) {
|
if (!contact || contact.projectId !== project.id) {
|
||||||
throw new NotFound("contact");
|
throw new NotFound("contact");
|
||||||
}
|
}
|
||||||
|
|
||||||
contact = await prisma.contact.update({
|
if (data) {
|
||||||
where: { id },
|
const givenUserData = Object.entries(data);
|
||||||
data: { email, subscribed, data: data ? JSON.stringify(data) : null },
|
const dataToUpdate = JSON.parse(contact.data ?? "{}");
|
||||||
include: {
|
|
||||||
triggers: { include: { event: true, action: true } },
|
givenUserData.forEach(([key, value]) => {
|
||||||
emails: { where: { subject: { not: null } } },
|
if (!value) {
|
||||||
|
delete dataToUpdate[key];
|
||||||
|
} else {
|
||||||
|
dataToUpdate[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.contact.update({
|
||||||
|
where: { id: contact.id },
|
||||||
|
data: { data: JSON.stringify(dataToUpdate) },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.contact.update({
|
||||||
|
where: { id: contact.id },
|
||||||
|
data: {
|
||||||
|
email,
|
||||||
|
subscribed: subscribed ?? contact.subscribed,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await redis.del(Keys.Project.contacts(project.id));
|
await redis.del(Keys.Project.contacts(project.id));
|
||||||
await redis.del(Keys.Contact.id(contact.id));
|
await redis.del(Keys.Contact.id(contact.id));
|
||||||
await redis.del(Keys.Contact.email(project.id, email));
|
await redis.del(Keys.Contact.email(project.id, contact.email));
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
+238
-225
@@ -1,264 +1,277 @@
|
|||||||
import {z} from 'zod';
|
import { TemplateStyle, TemplateType } from "@prisma/client";
|
||||||
import {TemplateStyle, TemplateType} from '@prisma/client';
|
import { z } from "zod";
|
||||||
|
|
||||||
const email = z
|
const email = z
|
||||||
.string({invalid_type_error: 'Email needs to be a string', required_error: 'Email is required'})
|
.string({ invalid_type_error: "Email needs to be a string", required_error: "Email is required" })
|
||||||
.email({message: 'Invalid email address'})
|
.email({ message: "Invalid email address" })
|
||||||
.transform(e => e.toLowerCase());
|
.transform((e) => e.toLowerCase());
|
||||||
|
|
||||||
const password = z.string().min(6, 'Password needs to be at least 6 characters long');
|
const password = z.string().min(6, "Password needs to be at least 6 characters long");
|
||||||
|
|
||||||
const id = z
|
const id = z
|
||||||
.string({invalid_type_error: 'ID needs to be a string', required_error: 'ID is required'})
|
.string({ invalid_type_error: "ID needs to be a string", required_error: "ID is required" })
|
||||||
.uuid({message: 'Id needs to be a valid UUID'});
|
.uuid({ message: "Id needs to be a valid UUID" });
|
||||||
|
|
||||||
export const UtilitySchemas = {
|
export const UtilitySchemas = {
|
||||||
id: z.object({
|
id: z.object({
|
||||||
id,
|
id,
|
||||||
}),
|
}),
|
||||||
email: z.object({
|
email: z.object({
|
||||||
email,
|
email,
|
||||||
}),
|
}),
|
||||||
pagination: z.object({
|
pagination: z.object({
|
||||||
page: z
|
page: z
|
||||||
.number({
|
.number({
|
||||||
invalid_type_error: 'Page needs to be a number',
|
invalid_type_error: "Page needs to be a number",
|
||||||
required_error: 'Page is required',
|
required_error: "Page is required",
|
||||||
})
|
})
|
||||||
.min(1, 'Page needs to be at least 1')
|
.min(1, "Page needs to be at least 1")
|
||||||
.default(1)
|
.default(1)
|
||||||
.or(
|
.or(
|
||||||
z.string().transform(s => {
|
z.string().transform((s) => {
|
||||||
return Number(s);
|
return Number(s);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const UserSchemas = {
|
export const UserSchemas = {
|
||||||
credentials: z.object({
|
credentials: z.object({
|
||||||
email: email,
|
email: email,
|
||||||
password: password,
|
password: password,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const zodSchema = z.record(
|
const zodSchema = z.record(
|
||||||
z.union(
|
z.union(
|
||||||
[
|
[
|
||||||
z
|
z
|
||||||
.string({
|
.string({
|
||||||
invalid_type_error:
|
invalid_type_error:
|
||||||
'Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)',
|
"Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)",
|
||||||
})
|
})
|
||||||
.transform(s => {
|
.transform((s) => {
|
||||||
return {persistent: true, value: s};
|
return { persistent: true, value: s };
|
||||||
}),
|
}),
|
||||||
z
|
z
|
||||||
.array(
|
.array(
|
||||||
z.string({
|
z.string({
|
||||||
invalid_type_error:
|
invalid_type_error:
|
||||||
'Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)',
|
"Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)",
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.transform(s => {
|
.transform((s) => {
|
||||||
return {persistent: true, value: s};
|
return { persistent: true, value: s };
|
||||||
}),
|
}),
|
||||||
z.object(
|
z.object(
|
||||||
{
|
{
|
||||||
persistent: z.boolean({invalid_type_error: 'Persistent should be a boolean'}).optional().default(true),
|
persistent: z.boolean({ invalid_type_error: "Persistent should be a boolean" }).optional().default(true),
|
||||||
value: z.union([z.string(), z.array(z.string())], {
|
value: z.union([z.string(), z.array(z.string())], {
|
||||||
invalid_type_error:
|
invalid_type_error:
|
||||||
'Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)',
|
"Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
invalid_type_error:
|
invalid_type_error:
|
||||||
'Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)',
|
"Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
invalid_type_error:
|
invalid_type_error:
|
||||||
'Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)',
|
"Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
{invalid_type_error: 'Metadata should be an object (https://docs.useplunk.com/working-with-contacts/metadata)'},
|
{ invalid_type_error: "Metadata should be an object (https://docs.useplunk.com/working-with-contacts/metadata)" },
|
||||||
);
|
);
|
||||||
export const EventSchemas = {
|
export const EventSchemas = {
|
||||||
post: z.object({
|
post: z.object({
|
||||||
email,
|
email,
|
||||||
subscribed: z
|
subscribed: z
|
||||||
.boolean({
|
.boolean({
|
||||||
invalid_type_error:
|
invalid_type_error:
|
||||||
'Subscribed should be a boolean. Read more: https://docs.useplunk.com/api-reference/actions/track',
|
"Subscribed should be a boolean. Read more: https://docs.useplunk.com/api-reference/actions/track",
|
||||||
})
|
})
|
||||||
.nullish(),
|
.nullish(),
|
||||||
event: z
|
event: z
|
||||||
.string({
|
.string({
|
||||||
required_error: 'Event is required. Read more: https://docs.useplunk.com/api-reference/actions/track',
|
required_error: "Event is required. Read more: https://docs.useplunk.com/api-reference/actions/track",
|
||||||
invalid_type_error:
|
invalid_type_error: "Event can only be a string. Read more: https://docs.useplunk.com/api-reference/actions/track",
|
||||||
'Event can only be a string. Read more: https://docs.useplunk.com/api-reference/actions/track',
|
})
|
||||||
})
|
.transform((n) => n.toLowerCase())
|
||||||
.transform(n => n.toLowerCase())
|
.transform((n) => n.replace(/ /g, "-")),
|
||||||
.transform(n => n.replace(/ /g, '-')),
|
data: zodSchema.nullish(),
|
||||||
data: zodSchema.nullish(),
|
}),
|
||||||
}),
|
send: z.object({
|
||||||
send: z.object({
|
subscribed: z.boolean({ invalid_type_error: "Subscribed should be a boolean" }).nullish(),
|
||||||
subscribed: z.boolean({invalid_type_error: 'Subscribed should be a boolean'}).nullish(),
|
from: email.nullish(),
|
||||||
from: email.nullish(),
|
name: z.string().nullish(),
|
||||||
name: z.string().nullish(),
|
reply: email.nullish(),
|
||||||
reply: email.nullish(),
|
to: z
|
||||||
to: z
|
.array(email)
|
||||||
.array(email)
|
.max(5, "You can only send transactional emails to 5 people at a time")
|
||||||
.max(5, 'You can only send transactional emails to 5 people at a time')
|
.or(email.transform((e) => [e])),
|
||||||
.or(email.transform(e => [e])),
|
subject: z.string({
|
||||||
subject: z.string({
|
required_error: "Subject is required. Read more: https://docs.useplunk.com/api-reference/transactional/send",
|
||||||
required_error: 'Subject is required. Read more: https://docs.useplunk.com/api-reference/transactional/send',
|
}),
|
||||||
}),
|
body: z.string({
|
||||||
body: z.string({
|
required_error: "Body is required. Read more: https://docs.useplunk.com/api-reference/transactional/send",
|
||||||
required_error: 'Body is required. Read more: https://docs.useplunk.com/api-reference/transactional/send',
|
}),
|
||||||
}),
|
headers: z.record(z.string()).nullish(),
|
||||||
headers: z.record(z.string()).nullish(),
|
}),
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CampaignSchemas = {
|
export const CampaignSchemas = {
|
||||||
send: z.object({
|
send: z.object({
|
||||||
id,
|
id,
|
||||||
live: z.boolean().default(false),
|
live: z.boolean().default(false),
|
||||||
delay: z.number().int('Delay needs to be a whole number').nonnegative('Delay needs to be a positive number'),
|
delay: z.number().int("Delay needs to be a whole number").nonnegative("Delay needs to be a positive number"),
|
||||||
}),
|
}),
|
||||||
create: z.object({
|
create: z.object({
|
||||||
subject: z
|
subject: z
|
||||||
.string()
|
.string()
|
||||||
.min(1, 'Subject needs to be at least 1 character long')
|
.min(1, "Subject needs to be at least 1 character long")
|
||||||
.max(70, 'Subject needs to be less than 70 characters long'),
|
.max(70, "Subject needs to be less than 70 characters long"),
|
||||||
body: z.string().min(1, 'Body needs to be at least 1 character long'),
|
body: z.string().min(1, "Body needs to be at least 1 character long"),
|
||||||
recipients: z.array(z.string()),
|
recipients: z.array(z.string()),
|
||||||
style: z.nativeEnum(TemplateStyle).default('PLUNK'),
|
style: z.nativeEnum(TemplateStyle).default("PLUNK"),
|
||||||
}),
|
}),
|
||||||
update: z.object({
|
update: z.object({
|
||||||
id,
|
id,
|
||||||
subject: z
|
subject: z
|
||||||
.string()
|
.string()
|
||||||
.min(1, 'Subject needs to be at least 1 character long')
|
.min(1, "Subject needs to be at least 1 character long")
|
||||||
.max(70, 'Subject needs to be less than 70 characters long'),
|
.max(70, "Subject needs to be less than 70 characters long"),
|
||||||
body: z.string().min(1, 'Body needs to be at least 1 character long'),
|
body: z.string().min(1, "Body needs to be at least 1 character long"),
|
||||||
recipients: z.array(z.string()),
|
recipients: z.array(z.string()),
|
||||||
style: z.nativeEnum(TemplateStyle).default('PLUNK'),
|
style: z.nativeEnum(TemplateStyle).default("PLUNK"),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ActionSchemas = {
|
export const ActionSchemas = {
|
||||||
create: z.object({
|
create: z.object({
|
||||||
name: z.string().min(1, 'Name needs to be at least 1 character long'),
|
name: z.string().min(1, "Name needs to be at least 1 character long"),
|
||||||
runOnce: z.boolean().default(false),
|
runOnce: z.boolean().default(false),
|
||||||
delay: z.number().int('Delay needs to be a whole number').nonnegative('Delay needs to be a positive number'),
|
delay: z.number().int("Delay needs to be a whole number").nonnegative("Delay needs to be a positive number"),
|
||||||
template: id,
|
template: id,
|
||||||
events: z.array(id).min(1, 'Select at least one event'),
|
events: z.array(id).min(1, "Select at least one event"),
|
||||||
notevents: z.array(id).optional().default([]),
|
notevents: z.array(id).optional().default([]),
|
||||||
}),
|
}),
|
||||||
update: z.object({
|
update: z.object({
|
||||||
id,
|
id,
|
||||||
name: z.string().min(1, 'Name needs to be at least 1 character long'),
|
name: z.string().min(1, "Name needs to be at least 1 character long"),
|
||||||
runOnce: z.boolean().default(false),
|
runOnce: z.boolean().default(false),
|
||||||
delay: z.number().int('Delay needs to be a whole number').nonnegative('Delay needs to be a positive number'),
|
delay: z.number().int("Delay needs to be a whole number").nonnegative("Delay needs to be a positive number"),
|
||||||
template: id,
|
template: id,
|
||||||
events: z.array(id).default([]),
|
events: z.array(id).default([]),
|
||||||
notevents: z.array(id).optional().default([]),
|
notevents: z.array(id).optional().default([]),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ContactSchemas = {
|
export const ContactSchemas = {
|
||||||
create: z.object({
|
create: z.object({
|
||||||
email,
|
email,
|
||||||
data: z
|
data: z
|
||||||
.object({})
|
.object({})
|
||||||
.catchall(z.union([z.string(), z.array(z.string())]))
|
.catchall(z.union([z.string(), z.array(z.string())]))
|
||||||
.or(z.string().transform(s => (s === '' ? null : JSON.parse(s))))
|
.or(z.string().transform((s) => (s === "" ? null : JSON.parse(s))))
|
||||||
.nullish(),
|
.nullish(),
|
||||||
subscribed: z.boolean(),
|
subscribed: z.boolean(),
|
||||||
}),
|
}),
|
||||||
update: z.object({
|
manage: z
|
||||||
id,
|
.object({
|
||||||
email,
|
id: id.optional(),
|
||||||
data: z
|
email: email.optional(),
|
||||||
.object({})
|
data: z
|
||||||
.catchall(z.union([z.string(), z.array(z.string())]))
|
.object({})
|
||||||
.or(z.string().transform(s => (s === '' ? null : JSON.parse(s))))
|
.catchall(z.union([z.string(), z.array(z.string()), z.null()]))
|
||||||
.nullish(),
|
.or(z.string().transform((s) => (s === "" ? null : JSON.parse(s))))
|
||||||
subscribed: z.boolean(),
|
.nullish(),
|
||||||
}),
|
subscribed: z.boolean().nullish(),
|
||||||
|
})
|
||||||
|
.refine(
|
||||||
|
(data) => {
|
||||||
|
return data.id || data.email;
|
||||||
|
},
|
||||||
|
{ message: "Either id or email should be specified" },
|
||||||
|
)
|
||||||
|
.refine(
|
||||||
|
(data) => {
|
||||||
|
// if id and email are both present
|
||||||
|
return !(data.id && data.email);
|
||||||
|
},
|
||||||
|
{ message: "Either id or email should be specified" },
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TemplateSchemas = {
|
export const TemplateSchemas = {
|
||||||
create: z.object({
|
create: z.object({
|
||||||
subject: z.string().min(1, "Subject can't be empty").max(70, 'Subject needs to be less than 70 characters long'),
|
subject: z.string().min(1, "Subject can't be empty").max(70, "Subject needs to be less than 70 characters long"),
|
||||||
body: z.string().min(1, "Body can't be empty"),
|
body: z.string().min(1, "Body can't be empty"),
|
||||||
type: z.nativeEnum(TemplateType).default('MARKETING'),
|
type: z.nativeEnum(TemplateType).default("MARKETING"),
|
||||||
style: z.nativeEnum(TemplateStyle).default('PLUNK'),
|
style: z.nativeEnum(TemplateStyle).default("PLUNK"),
|
||||||
}),
|
}),
|
||||||
update: z.object({
|
update: z.object({
|
||||||
id,
|
id,
|
||||||
subject: z.string().min(1, "Subject can't be empty").max(70, 'Subject needs to be less than 70 characters long'),
|
subject: z.string().min(1, "Subject can't be empty").max(70, "Subject needs to be less than 70 characters long"),
|
||||||
body: z.string().min(1, "Body can't be empty"),
|
body: z.string().min(1, "Body can't be empty"),
|
||||||
type: z.nativeEnum(TemplateType).default('MARKETING'),
|
type: z.nativeEnum(TemplateType).default("MARKETING"),
|
||||||
style: z.nativeEnum(TemplateStyle).default('PLUNK'),
|
style: z.nativeEnum(TemplateStyle).default("PLUNK"),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MembershipSchemas = {
|
export const MembershipSchemas = {
|
||||||
invite: z.object({
|
invite: z.object({
|
||||||
id,
|
id,
|
||||||
email,
|
email,
|
||||||
role: z.enum(['MEMBER', 'ADMIN']).default('MEMBER'),
|
role: z.enum(["MEMBER", "ADMIN"]).default("MEMBER"),
|
||||||
}),
|
}),
|
||||||
kick: z.object({
|
kick: z.object({
|
||||||
id,
|
id,
|
||||||
email,
|
email,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ProjectSchemas = {
|
export const ProjectSchemas = {
|
||||||
secret: z.object({
|
secret: z.object({
|
||||||
secret: z.string(),
|
secret: z.string(),
|
||||||
}),
|
}),
|
||||||
create: z.object({
|
create: z.object({
|
||||||
name: z.string().min(1, "Name can't be empty"),
|
name: z.string().min(1, "Name can't be empty"),
|
||||||
|
|
||||||
url: z
|
url: z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^(?:(?:https?):\/\/)?(?:[\w-]+\.)+[a-z]{2,}(?:\/[^\s]*)?$/)
|
.regex(/^(?:(?:https?):\/\/)?(?:[\w-]+\.)+[a-z]{2,}(?:\/[^\s]*)?$/)
|
||||||
.transform(u => (u.startsWith('http') ? u : `https://${u}`)),
|
.transform((u) => (u.startsWith("http") ? u : `https://${u}`)),
|
||||||
}),
|
}),
|
||||||
update: z.object({
|
update: z.object({
|
||||||
id: id,
|
id: id,
|
||||||
name: z.string().min(1, "Name can't be empty"),
|
name: z.string().min(1, "Name can't be empty"),
|
||||||
|
|
||||||
url: z
|
url: z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^(?:(?:https?):\/\/)?(?:[\w-]+\.)+[a-z]{2,}(?:\/[^\s]*)?$/)
|
.regex(/^(?:(?:https?):\/\/)?(?:[\w-]+\.)+[a-z]{2,}(?:\/[^\s]*)?$/)
|
||||||
.transform(u => (u.startsWith('http') ? u : `https://${u}`)),
|
.transform((u) => (u.startsWith("http") ? u : `https://${u}`)),
|
||||||
}),
|
}),
|
||||||
analytics: z.object({
|
analytics: z.object({
|
||||||
method: z.enum(['week', 'month', 'year']).default('week'),
|
method: z.enum(["week", "month", "year"]).default("week"),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentitySchemas = {
|
export const IdentitySchemas = {
|
||||||
create: z.object({
|
create: z.object({
|
||||||
id: id,
|
id: id,
|
||||||
email: email.refine(
|
email: email.refine(
|
||||||
e => {
|
(e) => {
|
||||||
return !['gmail.com', 'outlook.com', 'hotmail.com', 'yahoo.com', 'useplunk.com', 'useplunk.dev'].includes(
|
return !["gmail.com", "outlook.com", "hotmail.com", "yahoo.com", "useplunk.com", "useplunk.dev"].includes(
|
||||||
e.split('@')[1],
|
e.split("@")[1],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
{message: 'Please use your own domain'},
|
{ message: "Please use your own domain" },
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
update: z.object({
|
update: z.object({
|
||||||
id: id,
|
id: id,
|
||||||
from: z.string().min(1, "Name can't be empty"),
|
from: z.string().min(1, "Name can't be empty"),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user