Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- AlterTable
ALTER TABLE "ApiKey" ADD COLUMN "publicId" TEXT;

-- AlterTable
ALTER TABLE "Domain" ADD COLUMN "publicId" TEXT;

-- AlterTable
ALTER TABLE "Team" ADD COLUMN "publicId" TEXT;

-- AlterTable
ALTER TABLE "User" ADD COLUMN "publicId" TEXT;

-- CreateIndex
CREATE UNIQUE INDEX "ApiKey_publicId_key" ON "ApiKey"("publicId");

-- CreateIndex
CREATE UNIQUE INDEX "Domain_publicId_key" ON "Domain"("publicId");

-- CreateIndex
CREATE UNIQUE INDEX "Team_publicId_key" ON "Team"("publicId");

-- CreateIndex
CREATE UNIQUE INDEX "User_publicId_key" ON "User"("publicId");
4 changes: 4 additions & 0 deletions apps/web/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ model VerificationToken {

model User {
id Int @id @default(autoincrement())
publicId String? @unique
name String?
email String? @unique
emailVerified DateTime?
Expand All @@ -100,6 +101,7 @@ enum Plan {

model Team {
id Int @id @default(autoincrement())
publicId String? @unique
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Expand Down Expand Up @@ -181,6 +183,7 @@ enum DomainStatus {

model Domain {
id Int @id @default(autoincrement())
publicId String? @unique
name String @unique
teamId Int
status DomainStatus @default(PENDING)
Expand Down Expand Up @@ -209,6 +212,7 @@ enum ApiPermission {

model ApiKey {
id Int @id @default(autoincrement())
publicId String? @unique
clientId String @unique
tokenHash String
partialToken String
Expand Down
12 changes: 8 additions & 4 deletions apps/web/src/app/(dashboard)/domains/[domainId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default function DomainItemPage({

const domainQuery = api.domain.getDomain.useQuery(
{
id: Number(domainId),
id: domainId,
},
{
refetchInterval: (q) => (q?.state.data?.isVerifying ? 10000 : false),
Expand All @@ -54,7 +54,7 @@ export default function DomainItemPage({

const handleVerify = () => {
verifyQuery.mutate(
{ id: Number(domainId) },
{ id: domainId },
{
onSettled: () => {
domainQuery.refetch();
Expand Down Expand Up @@ -154,7 +154,9 @@ export default function DomainItemPage({
/>
</TableCell>
<TableCell className="">{record.ttl}</TableCell>
<TableCell className="">{record.priority ?? ""}</TableCell>
<TableCell className="">
{record.priority ?? ""}
</TableCell>
<TableCell className="">
<DnsVerificationStatus status={record.status} />
</TableCell>
Expand Down Expand Up @@ -248,7 +250,9 @@ const DomainSettings: React.FC<{ domain: DomainResponse }> = ({ domain }) => {
);
};

const DnsVerificationStatus: React.FC<{ status: DomainStatus }> = ({ status }) => {
const DnsVerificationStatus: React.FC<{ status: DomainStatus }> = ({
status,
}) => {
let badgeColor = "bg-gray/10 text-gray border-gray/10"; // Default color
switch (status) {
case DomainStatus.SUCCESS:
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/app/(dashboard)/domains/add-domain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ export default function AddDomain() {
{
onSuccess: async (data) => {
utils.domain.domains.invalidate();
await router.push(`/domains/${data.id}`);
await router.push(`/domains/${data.publicId ?? data.id}`);
setOpen(false);
},
onError: async (error) => {
toast.error(error.message);
},
}
},
);
}

Expand Down
8 changes: 4 additions & 4 deletions apps/web/src/app/(dashboard)/domains/domain-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const DomainItem: React.FC<{ domain: Domain }> = ({ domain }) => {
const utils = api.useUtils();

const [clickTracking, setClickTracking] = React.useState(
domain.clickTracking
domain.clickTracking,
);
const [openTracking, setOpenTracking] = React.useState(domain.openTracking);

Expand All @@ -52,7 +52,7 @@ const DomainItem: React.FC<{ domain: Domain }> = ({ domain }) => {
onSuccess: () => {
utils.domain.domains.invalidate();
},
}
},
);
}

Expand All @@ -64,7 +64,7 @@ const DomainItem: React.FC<{ domain: Domain }> = ({ domain }) => {
onSuccess: () => {
utils.domain.domains.invalidate();
},
}
},
);
}

Expand All @@ -75,7 +75,7 @@ const DomainItem: React.FC<{ domain: Domain }> = ({ domain }) => {
<div className="flex justify-between w-full pl-8 py-4">
<div className="flex flex-col gap-4 w-1/5">
<Link
href={`/domains/${domain.id}`}
href={`/domains/${domain.publicId ?? domain.id}`}
className="text-lg font-medium underline underline-offset-4 decoration-dashed"
>
{domain.name}
Expand Down
22 changes: 10 additions & 12 deletions apps/web/src/lib/zod/domain-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,12 @@ export const DomainDnsRecordSchema = z.object({
description: "DNS record type",
example: "TXT",
}),
name: z
.string()
.openapi({ description: "DNS record name", example: "mail" }),
value: z
.string()
.openapi({
description: "DNS record value",
example: "v=spf1 include:amazonses.com ~all",
}),
ttl: z
.string()
.openapi({ description: "DNS record TTL", example: "Auto" }),
name: z.string().openapi({ description: "DNS record name", example: "mail" }),
value: z.string().openapi({
description: "DNS record value",
example: "v=spf1 include:amazonses.com ~all",
}),
ttl: z.string().openapi({ description: "DNS record TTL", example: "Auto" }),
priority: z
.string()
.nullish()
Expand All @@ -33,6 +27,10 @@ export const DomainDnsRecordSchema = z.object({

export const DomainSchema = z.object({
id: z.number().openapi({ description: "The ID of the domain", example: 1 }),
publicId: z.string().nullable().optional().openapi({
description: "Public domain identifier",
example: "dom_3NfPq7hK9a2Tj6Rx",
}),
name: z
.string()
.openapi({ description: "The name of the domain", example: "example.com" }),
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/server/api/routers/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function formatDisplayNameFromEmail(email: string) {

const teamAdminSelection = {
id: true,
publicId: true,
name: true,
plan: true,
apiRateLimit: true,
Expand All @@ -55,6 +56,7 @@ const teamAdminSelection = {
domains: {
select: {
id: true,
publicId: true,
name: true,
status: true,
isVerifying: true,
Expand Down Expand Up @@ -347,6 +349,9 @@ export const adminRouter = createTRPCRouter({
OR: [
{ name: { equals: query, mode: "insensitive" } },
{ billingEmail: { equals: query, mode: "insensitive" } },
{
publicId: { equals: query, mode: "insensitive" },
},
{
teamUsers: {
some: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ describe("campaignRouter.duplicateCampaign", () => {

expect(mockDb.campaign.create).toHaveBeenCalledWith({
data: {
id: expect.stringMatching(/^cmp_/),
name: "Weekly update (Copy)",
from: "Team <hello@example.com>",
replyTo: ["support@example.com"],
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/server/api/routers/campaign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
getDocumentUploadUrl,
isStorageConfigured,
} from "~/server/service/storage-service";
import { newId } from "~/server/id";

const statuses = Object.values(CampaignStatus) as [CampaignStatus];

Expand Down Expand Up @@ -102,6 +103,7 @@ export const campaignRouter = createTRPCRouter({

const campaign = await db.campaign.create({
data: {
id: newId("campaign"),
...input,
teamId: team.id,
domainId: domain.id,
Expand Down Expand Up @@ -241,6 +243,7 @@ export const campaignRouter = createTRPCRouter({
async ({ ctx: { db, team, campaign } }) => {
const newCampaign = await db.campaign.create({
data: {
id: newId("campaign"),
name: `${campaign.name} (Copy)`,
from: campaign.from,
replyTo: campaign.replyTo,
Expand Down
33 changes: 12 additions & 21 deletions apps/web/src/server/api/routers/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
protectedProcedure,
domainProcedure,
} from "~/server/api/trpc";
import { db } from "~/server/db";
import {
createDomain,
deleteDomain,
Expand All @@ -30,13 +29,13 @@ export const domainRouter = createTRPCRouter({
ctx.team.id,
input.name,
input.region,
ctx.team.sesTenantId ?? undefined
ctx.team.sesTenantId ?? undefined,
);
}),

startVerification: domainProcedure.mutation(async ({ ctx, input }) => {
startVerification: domainProcedure.mutation(async ({ ctx }) => {
await ctx.db.domain.update({
where: { id: input.id },
where: { id: ctx.domain.id },
data: { isVerifying: true },
});
}),
Expand All @@ -45,26 +44,26 @@ export const domainRouter = createTRPCRouter({
return getDomains(ctx.team.id);
}),

getDomain: domainProcedure.query(async ({ input, ctx }) => {
return getDomain(input.id, ctx.team.id);
getDomain: domainProcedure.query(async ({ ctx }) => {
return getDomain(ctx.domain.id, ctx.team.id);
}),

updateDomain: domainProcedure
.input(
z.object({
clickTracking: z.boolean().optional(),
openTracking: z.boolean().optional(),
})
}),
)
.mutation(async ({ input }) => {
return updateDomain(input.id, {
.mutation(async ({ input, ctx }) => {
return updateDomain(ctx.domain.id, {
clickTracking: input.clickTracking,
openTracking: input.openTracking,
});
}),

deleteDomain: domainProcedure.mutation(async ({ input }) => {
await deleteDomain(input.id);
deleteDomain: domainProcedure.mutation(async ({ ctx }) => {
await deleteDomain(ctx.domain.id);
return { success: true };
}),

Expand All @@ -73,17 +72,9 @@ export const domainRouter = createTRPCRouter({
ctx: {
session: { user },
team,
domain,
},
input,
}) => {
const domain = await db.domain.findFirst({
where: { id: input.id, teamId: team.id },
});

if (!domain) {
throw new Error("Domain not found");
}

if (!user.email) {
throw new Error("User email not found");
}
Expand All @@ -96,6 +87,6 @@ export const domainRouter = createTRPCRouter({
text: "hello,\n\nuseSend is the best open source sending platform\n\ncheck out https://usesend.com",
html: "<p>hello,</p><p>useSend is the best open source sending platform<p><p>check out <a href='https://usesend.com'>usesend.com</a>",
});
}
},
),
});
3 changes: 3 additions & 0 deletions apps/web/src/server/api/routers/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
getDocumentUploadUrl,
isStorageConfigured,
} from "~/server/service/storage-service";
import { newId } from "~/server/id";

export const templateRouter = createTRPCRouter({
getTemplates: teamProcedure
Expand Down Expand Up @@ -64,6 +65,7 @@ export const templateRouter = createTRPCRouter({
.mutation(async ({ ctx: { db, team }, input }) => {
const template = await db.template.create({
data: {
id: newId("template"),
...input,
teamId: team.id,
},
Expand Down Expand Up @@ -134,6 +136,7 @@ export const templateRouter = createTRPCRouter({
async ({ ctx: { db, team, template }, input }) => {
const newTemplate = await db.template.create({
data: {
id: newId("template"),
name: `${template.name} (Copy)`,
subject: template.subject,
content: template.content,
Expand Down
Loading