chore: format
This commit is contained in:
@@ -1,22 +1,24 @@
|
||||
import jwt from "jsonwebtoken"
|
||||
import bcrypt from "bcryptjs"
|
||||
import { env } from "../constants"
|
||||
import jwt from "jsonwebtoken";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { env } from "../constants";
|
||||
|
||||
export async function hashPassword(password: string) {
|
||||
return bcrypt.hash(password, 10)
|
||||
return bcrypt.hash(password, 10);
|
||||
}
|
||||
|
||||
export async function comparePasswords(
|
||||
password: string,
|
||||
hashedPassword: string
|
||||
password: string,
|
||||
hashedPassword: string,
|
||||
) {
|
||||
return bcrypt.compare(password, hashedPassword)
|
||||
return bcrypt.compare(password, hashedPassword);
|
||||
}
|
||||
|
||||
export function generateToken(userId: string, version: number) {
|
||||
return jwt.sign({ id: userId, version }, env.JWT_SECRET, { expiresIn: "30d" })
|
||||
return jwt.sign({ id: userId, version }, env.JWT_SECRET, {
|
||||
expiresIn: "30d",
|
||||
});
|
||||
}
|
||||
|
||||
export function verifyToken(token: string) {
|
||||
return jwt.verify(token, env.JWT_SECRET)
|
||||
return jwt.verify(token, env.JWT_SECRET);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
const formatLog = (messages: unknown[]) => {
|
||||
return `[${new Date().toISOString()}] ${messages.join(" ")}`
|
||||
}
|
||||
return `[${new Date().toISOString()}] ${messages.join(" ")}`;
|
||||
};
|
||||
|
||||
export const logger = {
|
||||
log(...messages: unknown[]) {
|
||||
console.log(formatLog(messages))
|
||||
},
|
||||
info(...messages: unknown[]) {
|
||||
console.log(formatLog(messages))
|
||||
},
|
||||
error(...messages: unknown[]) {
|
||||
console.error(formatLog(messages))
|
||||
},
|
||||
warn(...messages: unknown[]) {
|
||||
console.warn(formatLog(messages))
|
||||
},
|
||||
}
|
||||
log(...messages: unknown[]) {
|
||||
console.log(formatLog(messages));
|
||||
},
|
||||
info(...messages: unknown[]) {
|
||||
console.log(formatLog(messages));
|
||||
},
|
||||
error(...messages: unknown[]) {
|
||||
console.error(formatLog(messages));
|
||||
},
|
||||
warn(...messages: unknown[]) {
|
||||
console.warn(formatLog(messages));
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export async function resolveProps<T extends Record<string, Promise<any>>>(
|
||||
promises: T
|
||||
promises: T,
|
||||
): Promise<{ [K in keyof T]: Awaited<T[K]> }> {
|
||||
const keys = Object.keys(promises)
|
||||
const values = await Promise.all(Object.values(promises))
|
||||
const keys = Object.keys(promises);
|
||||
const values = await Promise.all(Object.values(promises));
|
||||
|
||||
return keys.reduce(
|
||||
(acc, key, index) => {
|
||||
acc[key as keyof T] = values[index]
|
||||
return acc
|
||||
},
|
||||
{} as { [K in keyof T]: Awaited<T[K]> }
|
||||
)
|
||||
return keys.reduce(
|
||||
(acc, key, index) => {
|
||||
acc[key as keyof T] = values[index];
|
||||
return acc;
|
||||
},
|
||||
{} as { [K in keyof T]: Awaited<T[K]> },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,122 +1,125 @@
|
||||
import { replacePlaceholders } from "./placeholder-parser"
|
||||
import { describe, it, expect } from "vitest"
|
||||
import { replacePlaceholders } from "./placeholder-parser";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
describe("replacePlaceholders", () => {
|
||||
it("should replace a single placeholder", () => {
|
||||
const template = "Hello {{subscriber.name}}!"
|
||||
const data = { "subscriber.name": "John" }
|
||||
expect(replacePlaceholders(template, data)).toBe("Hello John!")
|
||||
})
|
||||
it("should replace a single placeholder", () => {
|
||||
const template = "Hello {{subscriber.name}}!";
|
||||
const data = { "subscriber.name": "John" };
|
||||
expect(replacePlaceholders(template, data)).toBe("Hello John!");
|
||||
});
|
||||
|
||||
it("should replace multiple placeholders", () => {
|
||||
const template = "Order for {{subscriber.name}} from {{organization.name}}."
|
||||
const data = {
|
||||
"subscriber.name": "Alice",
|
||||
"organization.name": "Org Inc",
|
||||
}
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Order for Alice from Org Inc."
|
||||
)
|
||||
})
|
||||
it("should replace multiple placeholders", () => {
|
||||
const template =
|
||||
"Order for {{subscriber.name}} from {{organization.name}}.";
|
||||
const data = {
|
||||
"subscriber.name": "Alice",
|
||||
"organization.name": "Org Inc",
|
||||
};
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Order for Alice from Org Inc.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle templates with no placeholders", () => {
|
||||
const template = "This is a static string."
|
||||
const data = { "subscriber.name": "Bob" }
|
||||
expect(replacePlaceholders(template, data)).toBe("This is a static string.")
|
||||
})
|
||||
it("should handle templates with no placeholders", () => {
|
||||
const template = "This is a static string.";
|
||||
const data = { "subscriber.name": "Bob" };
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"This is a static string.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle empty data", () => {
|
||||
const template = "Hello {{subscriber.name}}!"
|
||||
const data = {}
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Hello {{subscriber.name}}!"
|
||||
)
|
||||
})
|
||||
it("should handle empty data", () => {
|
||||
const template = "Hello {{subscriber.name}}!";
|
||||
const data = {};
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Hello {{subscriber.name}}!",
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle empty template string", () => {
|
||||
const template = ""
|
||||
const data = { "subscriber.name": "Eve" }
|
||||
expect(replacePlaceholders(template, data)).toBe("")
|
||||
})
|
||||
it("should handle empty template string", () => {
|
||||
const template = "";
|
||||
const data = { "subscriber.name": "Eve" };
|
||||
expect(replacePlaceholders(template, data)).toBe("");
|
||||
});
|
||||
|
||||
it("should handle placeholders with special characters in keys", () => {
|
||||
const template = "Link: {{unsubscribe_link}}"
|
||||
const data = { unsubscribe_link: "http://example.com/unsubscribe" }
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Link: http://example.com/unsubscribe"
|
||||
)
|
||||
})
|
||||
it("should handle placeholders with special characters in keys", () => {
|
||||
const template = "Link: {{unsubscribe_link}}";
|
||||
const data = { unsubscribe_link: "http://example.com/unsubscribe" };
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Link: http://example.com/unsubscribe",
|
||||
);
|
||||
});
|
||||
|
||||
it("should replace all occurrences of a placeholder", () => {
|
||||
const template = "Hi {{subscriber.name}}, welcome {{subscriber.name}}."
|
||||
const data = { "subscriber.name": "Charlie" }
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Hi Charlie, welcome Charlie."
|
||||
)
|
||||
})
|
||||
it("should replace all occurrences of a placeholder", () => {
|
||||
const template = "Hi {{subscriber.name}}, welcome {{subscriber.name}}.";
|
||||
const data = { "subscriber.name": "Charlie" };
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Hi Charlie, welcome Charlie.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should not replace partial matches", () => {
|
||||
const template = "Hello {{subscriber.name}} and {{subscriber.names}}"
|
||||
const data = { "subscriber.name": "David" }
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Hello David and {{subscriber.names}}"
|
||||
)
|
||||
})
|
||||
it("should not replace partial matches", () => {
|
||||
const template = "Hello {{subscriber.name}} and {{subscriber.names}}";
|
||||
const data = { "subscriber.name": "David" };
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Hello David and {{subscriber.names}}",
|
||||
);
|
||||
});
|
||||
|
||||
it("should correctly replace various types of placeholders", () => {
|
||||
const template =
|
||||
"Email: {{subscriber.email}}, Campaign: {{campaign.name}}, Org: {{organization.name}}, Unsub: {{unsubscribe_link}}, Date: {{current_date}}"
|
||||
const data = {
|
||||
"subscriber.email": "test@example.com",
|
||||
"campaign.name": "Newsletter Q1",
|
||||
"organization.name": "MyCompany",
|
||||
unsubscribe_link: "domain.com/unsub",
|
||||
current_date: "2024-01-01",
|
||||
}
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Email: test@example.com, Campaign: Newsletter Q1, Org: MyCompany, Unsub: domain.com/unsub, Web: domain.com/web, Date: 2024-01-01"
|
||||
)
|
||||
})
|
||||
it("should correctly replace various types of placeholders", () => {
|
||||
const template =
|
||||
"Email: {{subscriber.email}}, Campaign: {{campaign.name}}, Org: {{organization.name}}, Unsub: {{unsubscribe_link}}, Date: {{current_date}}";
|
||||
const data = {
|
||||
"subscriber.email": "test@example.com",
|
||||
"campaign.name": "Newsletter Q1",
|
||||
"organization.name": "MyCompany",
|
||||
unsubscribe_link: "domain.com/unsub",
|
||||
current_date: "2024-01-01",
|
||||
};
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Email: test@example.com, Campaign: Newsletter Q1, Org: MyCompany, Unsub: domain.com/unsub, Web: domain.com/web, Date: 2024-01-01",
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle data with undefined values gracefully", () => {
|
||||
const template = "Hello {{subscriber.name}} and {{campaign.name}}!"
|
||||
const data = {
|
||||
"subscriber.name": "DefinedName",
|
||||
"campaign.name": undefined,
|
||||
} as { [key: string]: string | undefined } // Added type assertion for clarity
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Hello DefinedName and {{campaign.name}}!"
|
||||
)
|
||||
})
|
||||
it("should handle data with undefined values gracefully", () => {
|
||||
const template = "Hello {{subscriber.name}} and {{campaign.name}}!";
|
||||
const data = {
|
||||
"subscriber.name": "DefinedName",
|
||||
"campaign.name": undefined,
|
||||
} as { [key: string]: string | undefined }; // Added type assertion for clarity
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Hello DefinedName and {{campaign.name}}!",
|
||||
);
|
||||
});
|
||||
|
||||
it("should replace placeholders with leading spaces inside braces", () => {
|
||||
const template = "Hello {{ subscriber.name }}!"
|
||||
const data = { "subscriber.name": "SpacedJohn" }
|
||||
expect(replacePlaceholders(template, data)).toBe("Hello SpacedJohn!")
|
||||
})
|
||||
it("should replace placeholders with leading spaces inside braces", () => {
|
||||
const template = "Hello {{ subscriber.name }}!";
|
||||
const data = { "subscriber.name": "SpacedJohn" };
|
||||
expect(replacePlaceholders(template, data)).toBe("Hello SpacedJohn!");
|
||||
});
|
||||
|
||||
it("should replace placeholders with trailing spaces inside braces", () => {
|
||||
const template = "Hello {{subscriber.name }}!"
|
||||
const data = { "subscriber.name": "SpacedAlice" }
|
||||
expect(replacePlaceholders(template, data)).toBe("Hello SpacedAlice!")
|
||||
})
|
||||
it("should replace placeholders with trailing spaces inside braces", () => {
|
||||
const template = "Hello {{subscriber.name }}!";
|
||||
const data = { "subscriber.name": "SpacedAlice" };
|
||||
expect(replacePlaceholders(template, data)).toBe("Hello SpacedAlice!");
|
||||
});
|
||||
|
||||
it("should replace placeholders with leading and trailing spaces inside braces", () => {
|
||||
const template = "Hello {{ subscriber.name }}!"
|
||||
const data = { "subscriber.name": "SpacedBob" }
|
||||
expect(replacePlaceholders(template, data)).toBe("Hello SpacedBob!")
|
||||
})
|
||||
it("should replace placeholders with leading and trailing spaces inside braces", () => {
|
||||
const template = "Hello {{ subscriber.name }}!";
|
||||
const data = { "subscriber.name": "SpacedBob" };
|
||||
expect(replacePlaceholders(template, data)).toBe("Hello SpacedBob!");
|
||||
});
|
||||
|
||||
it("should replace multiple placeholders with various spacing", () => {
|
||||
const template =
|
||||
"Hi {{subscriber.name}}, welcome {{ organization.name }}. Date: {{current_date}}."
|
||||
const data = {
|
||||
"subscriber.name": "SpacedEve",
|
||||
"organization.name": "Org Spaced Inc.",
|
||||
current_date: "2024-02-20",
|
||||
}
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Hi SpacedEve, welcome Org Spaced Inc.. Date: 2024-02-20."
|
||||
)
|
||||
})
|
||||
})
|
||||
it("should replace multiple placeholders with various spacing", () => {
|
||||
const template =
|
||||
"Hi {{subscriber.name}}, welcome {{ organization.name }}. Date: {{current_date}}.";
|
||||
const data = {
|
||||
"subscriber.name": "SpacedEve",
|
||||
"organization.name": "Org Spaced Inc.",
|
||||
current_date: "2024-02-20",
|
||||
};
|
||||
expect(replacePlaceholders(template, data)).toBe(
|
||||
"Hi SpacedEve, welcome Org Spaced Inc.. Date: 2024-02-20.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
interface SubscriberPlaceholderData {
|
||||
email: string
|
||||
name?: string
|
||||
email: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
interface CampaignPlaceholderData {
|
||||
name: string
|
||||
subject?: string
|
||||
name: string;
|
||||
subject?: string;
|
||||
}
|
||||
|
||||
interface OrganizationPlaceholderData {
|
||||
name: string
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface PlaceholderData {
|
||||
subscriber: SubscriberPlaceholderData
|
||||
campaign: CampaignPlaceholderData
|
||||
organization: OrganizationPlaceholderData
|
||||
unsubscribe_link: string
|
||||
current_date?: string
|
||||
subscriber: SubscriberPlaceholderData;
|
||||
campaign: CampaignPlaceholderData;
|
||||
organization: OrganizationPlaceholderData;
|
||||
unsubscribe_link: string;
|
||||
current_date?: string;
|
||||
}
|
||||
|
||||
export type PlaceholderDataKey =
|
||||
| `subscriber.${keyof SubscriberPlaceholderData}`
|
||||
| `campaign.${keyof CampaignPlaceholderData}`
|
||||
| `organization.${keyof OrganizationPlaceholderData}`
|
||||
| `unsubscribe_link`
|
||||
| `current_date`
|
||||
| `subscriber.metadata.${string}`
|
||||
| `subscriber.${keyof SubscriberPlaceholderData}`
|
||||
| `campaign.${keyof CampaignPlaceholderData}`
|
||||
| `organization.${keyof OrganizationPlaceholderData}`
|
||||
| `unsubscribe_link`
|
||||
| `current_date`
|
||||
| `subscriber.metadata.${string}`;
|
||||
|
||||
export function replacePlaceholders(
|
||||
template: string,
|
||||
data: Partial<Record<PlaceholderDataKey, string>>
|
||||
template: string,
|
||||
data: Partial<Record<PlaceholderDataKey, string>>,
|
||||
): string {
|
||||
let result = template
|
||||
for (const key in data) {
|
||||
const placeholderRegex = new RegExp(
|
||||
`{{\\s*${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*}}`,
|
||||
"g"
|
||||
)
|
||||
const value = data[key as PlaceholderDataKey]
|
||||
if (value !== undefined) {
|
||||
result = result.replace(placeholderRegex, value)
|
||||
}
|
||||
}
|
||||
return result
|
||||
let result = template;
|
||||
for (const key in data) {
|
||||
const placeholderRegex = new RegExp(
|
||||
`{{\\s*${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*}}`,
|
||||
"g",
|
||||
);
|
||||
const value = data[key as PlaceholderDataKey];
|
||||
if (value !== undefined) {
|
||||
result = result.replace(placeholderRegex, value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import { PrismaClient } from "../../prisma/client"
|
||||
import { PrismaClient } from "../../prisma/client";
|
||||
|
||||
export const prisma = new PrismaClient({
|
||||
omit: {
|
||||
user: {
|
||||
password: true,
|
||||
pwdVersion: true,
|
||||
},
|
||||
apiKey: {
|
||||
key: true,
|
||||
},
|
||||
},
|
||||
omit: {
|
||||
user: {
|
||||
password: true,
|
||||
pwdVersion: true,
|
||||
},
|
||||
apiKey: {
|
||||
key: true,
|
||||
},
|
||||
},
|
||||
}).$extends({
|
||||
query: {
|
||||
subscriber: {
|
||||
$allOperations({ args, query }) {
|
||||
if (
|
||||
"data" in args &&
|
||||
"email" in args.data &&
|
||||
typeof args.data.email === "string"
|
||||
) {
|
||||
args.data.email = args.data.email.toLowerCase()
|
||||
}
|
||||
return query(args)
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
query: {
|
||||
subscriber: {
|
||||
$allOperations({ args, query }) {
|
||||
if (
|
||||
"data" in args &&
|
||||
"email" in args.data &&
|
||||
typeof args.data.email === "string"
|
||||
) {
|
||||
args.data.email = args.data.email.toLowerCase();
|
||||
}
|
||||
return query(args);
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { z } from "zod"
|
||||
import { z } from "zod";
|
||||
|
||||
export const paginationSchema = z.object({
|
||||
page: z.number().min(1).default(1),
|
||||
perPage: z.number().min(1).max(100).default(10),
|
||||
search: z.string().optional(),
|
||||
})
|
||||
page: z.number().min(1).default(1),
|
||||
perPage: z.number().min(1).max(100).default(10),
|
||||
search: z.string().optional(),
|
||||
});
|
||||
|
||||
export type PaginationSchema = z.infer<typeof paginationSchema>
|
||||
export type PaginationSchema = z.infer<typeof paginationSchema>;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { z } from "zod"
|
||||
import { z } from "zod";
|
||||
|
||||
export const tokenPayloadSchema = z.object({
|
||||
id: z.string(),
|
||||
version: z.number(),
|
||||
iat: z.number(),
|
||||
exp: z.number(),
|
||||
})
|
||||
id: z.string(),
|
||||
version: z.number(),
|
||||
iat: z.number(),
|
||||
exp: z.number(),
|
||||
});
|
||||
|
||||
export type TokenPayload = z.infer<typeof tokenPayloadSchema>
|
||||
export type TokenPayload = z.infer<typeof tokenPayloadSchema>;
|
||||
|
||||
Reference in New Issue
Block a user