Skip to content
Open
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
4 changes: 2 additions & 2 deletions apps/web/src/server/public-api/api-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ function statusToCode(status: StatusCode): z.infer<typeof ErrorCode> {
}
}

export class UnsendApiError extends HTTPException {
export class UseSendApiError extends HTTPException {
public readonly code: z.infer<typeof ErrorCode>;

constructor({
Expand All @@ -78,7 +78,7 @@ export function handleError(err: Error, c: Context): Response {
/**
* We can handle this very well, as it is something we threw ourselves
*/
if (err instanceof UnsendApiError) {
if (err instanceof UseSendApiError) {
if (err.status >= 500) {
logger.error(
{ name: err.name, code: err.code, status: err.status, err },
Expand Down
10 changes: 5 additions & 5 deletions apps/web/src/server/public-api/api-utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Context } from "hono";
import { db } from "../db";
import { UnsendApiError } from "./api-error";
import { UseSendApiError } from "./api-error";

export const getContactBook = async (c: Context, teamId: number) => {
const contactBookId = c.req.param("contactBookId");

if (!contactBookId) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "BAD_REQUEST",
message: "contactBookId is mandatory",
});
Expand All @@ -17,7 +17,7 @@ export const getContactBook = async (c: Context, teamId: number) => {
});

if (!contactBook) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "NOT_FOUND",
message: "Contact book not found for this team",
});
Expand All @@ -30,7 +30,7 @@ export const checkIsValidEmailId = async (emailId: string, teamId: number) => {
const email = await db.email.findUnique({ where: { id: emailId, teamId } });

if (!email) {
throw new UnsendApiError({ code: "NOT_FOUND", message: "Email not found" });
throw new UseSendApiError({ code: "NOT_FOUND", message: "Email not found" });
}
};

Expand All @@ -51,7 +51,7 @@ export const checkIsValidEmailIdWithDomainRestriction = async (
const email = await db.email.findUnique({ where: whereClause });

if (!email) {
throw new UnsendApiError({ code: "NOT_FOUND", message: "Email not found" });
throw new UseSendApiError({ code: "NOT_FOUND", message: "Email not found" });
}

return email;
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/server/public-api/api/contacts/get-contact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createRoute, z } from "@hono/zod-openapi";
import { PublicAPIApp } from "~/server/public-api/hono";
import { getTeamFromToken } from "~/server/public-api/auth";
import { db } from "~/server/db";
import { UnsendApiError } from "../../api-error";
import { UseSendApiError } from "../../api-error";
import { getContactBook } from "../../api-utils";

const route = createRoute({
Expand Down Expand Up @@ -63,7 +63,7 @@ function getContact(app: PublicAPIApp) {
});

if (!contact) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "NOT_FOUND",
message: "Contact not found",
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createRoute, z } from "@hono/zod-openapi";
import { PublicAPIApp } from "~/server/public-api/hono";
import { getTeamFromToken } from "~/server/public-api/auth";
import { db } from "~/server/db";
import { UnsendApiError } from "../../api-error";
import { UseSendApiError } from "../../api-error";
import { getContactBook } from "../../api-utils";

const route = createRoute({
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/server/public-api/api/domains/delete-domain.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createRoute, z } from "@hono/zod-openapi";
import { PublicAPIApp } from "../../hono";
import { db } from "~/server/db";
import { UnsendApiError } from "../../api-error";
import { UseSendApiError } from "../../api-error";
import { deleteDomain as deleteDomainService } from "~/server/service/domain-service";

const route = createRoute({
Expand Down Expand Up @@ -61,7 +61,7 @@ function deleteDomain(app: PublicAPIApp) {

// Enforce API key domain restriction
if (team.apiKey.domainId && team.apiKey.domainId !== domainId) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "FORBIDDEN",
message: "API key doesn't have access to this domain",
});
Expand All @@ -75,7 +75,7 @@ function deleteDomain(app: PublicAPIApp) {
});

if (!domain) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "NOT_FOUND",
message: "Domain not found",
});
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/server/public-api/api/domains/get-domain.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createRoute, z } from "@hono/zod-openapi";
import { DomainSchema } from "~/lib/zod/domain-schema";
import { PublicAPIApp } from "~/server/public-api/hono";
import { UnsendApiError } from "../../api-error";
import { UseSendApiError } from "../../api-error";
import { db } from "~/server/db";
import { getDomain as getDomainService } from "~/server/service/domain-service";

Expand Down Expand Up @@ -35,7 +35,7 @@ function getDomain(app: PublicAPIApp) {

// Enforce API key domain restriction (if any)
if (team.apiKey.domainId && team.apiKey.domainId !== id) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "NOT_FOUND",
message: "Domain not found",
});
Expand All @@ -46,7 +46,7 @@ function getDomain(app: PublicAPIApp) {
try {
enriched = await getDomainService(id, team.id);
} catch (e) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "INTERNAL_SERVER_ERROR",
message: e instanceof Error ? e.message : "Internal server error",
});
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/server/public-api/api/emails/get-email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { PublicAPIApp } from "~/server/public-api/hono";
import { getTeamFromToken } from "~/server/public-api/auth";
import { db } from "~/server/db";
import { EmailStatus } from "@prisma/client";
import { UnsendApiError } from "../../api-error";
import { UseSendApiError } from "../../api-error";

const route = createRoute({
method: "get",
Expand Down Expand Up @@ -88,7 +88,7 @@ function send(app: PublicAPIApp) {
});

if (!email) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "NOT_FOUND",
message: "Email not found",
});
Expand Down
10 changes: 5 additions & 5 deletions apps/web/src/server/public-api/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Context } from "hono";
import { db } from "../db";
import { UnsendApiError } from "./api-error";
import { UseSendApiError } from "./api-error";
import { getTeamAndApiKey } from "../service/api-service";
import { isSelfHosted } from "~/utils/common";
import { logger } from "../logger/log";
Expand All @@ -12,7 +12,7 @@ export const getTeamFromToken = async (c: Context) => {
const authHeader = c.req.header("Authorization");

if (!authHeader) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "UNAUTHORIZED",
message: "No Authorization header provided",
});
Expand All @@ -21,7 +21,7 @@ export const getTeamFromToken = async (c: Context) => {
const token = authHeader.split(" ")[1];

if (!token) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "UNAUTHORIZED",
message: "No Authorization header provided",
});
Expand All @@ -30,7 +30,7 @@ export const getTeamFromToken = async (c: Context) => {
const teamAndApiKey = await getTeamAndApiKey(token);

if (!teamAndApiKey) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "FORBIDDEN",
message: "Invalid API token",
});
Expand All @@ -39,7 +39,7 @@ export const getTeamFromToken = async (c: Context) => {
const { team, apiKey } = teamAndApiKey;

if (!team) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "FORBIDDEN",
message: "Invalid API token",
});
Expand Down
8 changes: 4 additions & 4 deletions apps/web/src/server/public-api/hono.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { env } from "~/env";
import { getRedis } from "~/server/redis";
import { getTeamFromToken } from "~/server/public-api/auth";
import { isSelfHosted } from "~/utils/common";
import { UnsendApiError } from "./api-error";
import { UseSendApiError } from "./api-error";
import { Team, ApiKey } from "@prisma/client";
import { logger } from "../logger/log";

Expand Down Expand Up @@ -36,11 +36,11 @@ export function getApp() {
const team = await getTeamFromToken(c as any);
c.set("team", team);
} catch (error) {
if (error instanceof UnsendApiError) {
if (error instanceof UseSendApiError) {
throw error;
}
logger.error({ err: error }, "Error in getTeamFromToken middleware");
throw new UnsendApiError({
throw new UseSendApiError({
code: "INTERNAL_SERVER_ERROR",
message: "Authentication failed",
});
Expand Down Expand Up @@ -104,7 +104,7 @@ export function getApp() {
"Retry-After",
String(ttl > 0 ? ttl : RATE_LIMIT_WINDOW_SECONDS)
);
throw new UnsendApiError({
throw new UseSendApiError({
code: "RATE_LIMITED",
message: `Rate limit exceeded. Try again in ${ttl > 0 ? ttl : RATE_LIMIT_WINDOW_SECONDS} seconds.`,
});
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/server/public-api/schemas/campaign-schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "@hono/zod-openapi";
import * as chrono from "chrono-node";
import { UnsendApiError } from "../api-error";
import { UseSendApiError } from "../api-error";

const stringOrStringArray = z.union([
z.string().min(1),
Expand All @@ -22,7 +22,7 @@ export const parseScheduledAt = (scheduledAt?: string): Date | undefined => {
return chronoDate;
}

throw new UnsendApiError({
throw new UseSendApiError({
code: "BAD_REQUEST",
message: `Invalid date format: ${scheduledAt}. Use ISO 8601 format or natural language like 'tomorrow 9am'.`,
});
Expand Down
30 changes: 15 additions & 15 deletions apps/web/src/server/service/campaign-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import { logger } from "../logger/log";
import { createWorkerHandler, TeamJob } from "../queue/bullmq-context";
import { SuppressionService } from "./suppression-service";
import { UnsendApiError } from "../public-api/api-error";
import { UseSendApiError } from "../public-api/api-error";
import {
validateApiKeyDomainAccess,
validateDomainFromEmail,
Expand Down Expand Up @@ -190,7 +190,7 @@ export async function createCampaignFromApi({
batchSize?: number;
}) {
if (!content && !html) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "BAD_REQUEST",
message: "Either content or html must be provided",
});
Expand All @@ -201,7 +201,7 @@ export async function createCampaignFromApi({
JSON.parse(content);
} catch (error) {
logger.error({ err: error }, "Invalid campaign content JSON from API");
throw new UnsendApiError({
throw new UseSendApiError({
code: "BAD_REQUEST",
message: "Invalid content JSON",
});
Expand All @@ -214,7 +214,7 @@ export async function createCampaignFromApi({
});

if (!contactBook) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "BAD_REQUEST",
message: "Contact book not found",
});
Expand All @@ -229,7 +229,7 @@ export async function createCampaignFromApi({
});

if (!apiKey || apiKey.teamId !== teamId) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "FORBIDDEN",
message: "Invalid API key",
});
Expand All @@ -249,7 +249,7 @@ export async function createCampaignFromApi({
);

if (!unsubPlaceholderFound) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "BAD_REQUEST",
message: "Campaign must include an unsubscribe link before sending",
});
Expand Down Expand Up @@ -319,7 +319,7 @@ export async function getCampaignForTeam({
});

if (!campaign) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "NOT_FOUND",
message: "Campaign not found",
});
Expand Down Expand Up @@ -396,7 +396,7 @@ export async function scheduleCampaign({
where: { id: campaignId, teamId },
});
if (!campaign) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "NOT_FOUND",
message: "Campaign not found",
});
Expand All @@ -408,21 +408,21 @@ export async function scheduleCampaign({
campaign = prepared.campaign;
html = prepared.html;
} catch (err) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "BAD_REQUEST",
message: err instanceof Error ? err.message : "Invalid campaign content",
});
}

if (!campaign.contactBookId) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "BAD_REQUEST",
message: "No contact book found for campaign",
});
}

if (!html) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "BAD_REQUEST",
message: "No HTML content for campaign",
});
Expand All @@ -433,7 +433,7 @@ export async function scheduleCampaign({
html
);
if (!unsubPlaceholderFound) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "BAD_REQUEST",
message: "Campaign must include an unsubscribe link before scheduling",
});
Expand All @@ -445,7 +445,7 @@ export async function scheduleCampaign({
});

if (total === 0) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "BAD_REQUEST",
message: "No subscribed contacts to send",
});
Expand Down Expand Up @@ -486,7 +486,7 @@ export async function pauseCampaign({
});

if (!campaign) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "NOT_FOUND",
message: "Campaign not found",
});
Expand All @@ -512,7 +512,7 @@ export async function resumeCampaign({
});

if (!campaign) {
throw new UnsendApiError({
throw new UseSendApiError({
code: "NOT_FOUND",
message: "Campaign not found",
});
Expand Down
Loading