Deps Update, CSSNano, Sentry env (#210)
1. Update dependencies 2. Use cssnano for production 3. Assign an environment variable to Sentry's environment settings 4. `Pill` now accepts React Nodes so we can pass translations using Trans component 5. Switch to mailpit API during tests 6. Do not require Email Sender to be of type email and add proper error messages
This commit is contained in:
committed by
GitHub
parent
bd723dccce
commit
a45fda44cf
@@ -127,7 +127,7 @@ class ConnectivityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const webhooksSecret = await getVariable(
|
const webhooksSecret = await getVariable(
|
||||||
'STRIPE_WEBHOOKS_SECRET',
|
'STRIPE_WEBHOOK_SECRET',
|
||||||
this.mode,
|
this.mode,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1045,6 +1045,39 @@ export const envVariables: EnvVariableModel[] = [
|
|||||||
return z.enum(['baselime', 'sentry', '']).optional().safeParse(value);
|
return z.enum(['baselime', 'sentry', '']).optional().safeParse(value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'NEXT_PUBLIC_SENTRY_DSN',
|
||||||
|
description: 'The Sentry DSN to use.',
|
||||||
|
category: 'Monitoring',
|
||||||
|
contextualValidation: {
|
||||||
|
dependencies: [
|
||||||
|
{
|
||||||
|
variable: 'NEXT_PUBLIC_MONITORING_PROVIDER',
|
||||||
|
condition: (value) => value === 'sentry',
|
||||||
|
message:
|
||||||
|
'NEXT_PUBLIC_SENTRY_DSN is required when NEXT_PUBLIC_MONITORING_PROVIDER is set to "sentry"',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
validate: ({ value }) => {
|
||||||
|
return z
|
||||||
|
.string()
|
||||||
|
.min(
|
||||||
|
1,
|
||||||
|
`The NEXT_PUBLIC_SENTRY_DSN variable must be at least 1 character`,
|
||||||
|
)
|
||||||
|
.safeParse(value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NEXT_PUBLIC_SENTRY_ENVIRONMENT',
|
||||||
|
description: 'The Sentry environment to use.',
|
||||||
|
category: 'Monitoring',
|
||||||
|
required: true,
|
||||||
|
validate: ({ value }) => {
|
||||||
|
return z.string().optional().safeParse(value);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'NEXT_PUBLIC_BASELIME_KEY',
|
name: 'NEXT_PUBLIC_BASELIME_KEY',
|
||||||
description: 'The Baselime key to use.',
|
description: 'The Baselime key to use.',
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^4.1.3",
|
"@hookform/resolvers": "^4.1.3",
|
||||||
"@tanstack/react-query": "5.67.1",
|
"@tanstack/react-query": "5.67.3",
|
||||||
"lucide-react": "^0.477.0",
|
"lucide-react": "^0.479.0",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"nodemailer": "^6.10.0",
|
"nodemailer": "^6.10.0",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0"
|
"react-dom": "19.0.0"
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"@kit/shared": "workspace:*",
|
"@kit/shared": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@tailwindcss/postcss": "^4.0.9",
|
"@tailwindcss/postcss": "^4.0.13",
|
||||||
"@types/node": "^22.13.4",
|
"@types/node": "^22.13.4",
|
||||||
"@types/nodemailer": "6.4.17",
|
"@types/nodemailer": "6.4.17",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"babel-plugin-react-compiler": "19.0.0-beta-e1e972c-20250221",
|
"babel-plugin-react-compiler": "19.0.0-beta-e1e972c-20250221",
|
||||||
"pino-pretty": "^13.0.0",
|
"pino-pretty": "^13.0.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
"tailwindcss": "4.0.9",
|
"tailwindcss": "4.0.13",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.50.1",
|
"@playwright/test": "^1.51.0",
|
||||||
"@types/node": "^22.13.4",
|
"@types/node": "^22.13.4",
|
||||||
"node-html-parser": "^7.0.1",
|
"node-html-parser": "^7.0.1",
|
||||||
"totp-generator": "^1.0.0"
|
"totp-generator": "^1.0.0"
|
||||||
|
|||||||
@@ -144,10 +144,12 @@ test.describe('Admin', () => {
|
|||||||
await page.getByTestId('admin-ban-account-button').click();
|
await page.getByTestId('admin-ban-account-button').click();
|
||||||
await page.fill('[placeholder="Type CONFIRM to confirm"]', 'CONFIRM');
|
await page.fill('[placeholder="Type CONFIRM to confirm"]', 'CONFIRM');
|
||||||
await page.getByRole('button', { name: 'Ban User' }).click();
|
await page.getByRole('button', { name: 'Ban User' }).click();
|
||||||
|
|
||||||
await expect(page.getByText('Banned')).toBeVisible();
|
await expect(page.getByText('Banned')).toBeVisible();
|
||||||
|
|
||||||
// Now reactivate
|
// Now reactivate
|
||||||
await page.getByTestId('admin-reactivate-account-button').click();
|
await page.getByTestId('admin-reactivate-account-button').click();
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('heading', { name: 'Reactivate User' }),
|
page.getByRole('heading', { name: 'Reactivate User' }),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
@@ -323,28 +325,30 @@ async function createUser(
|
|||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
const auth = new AuthPageObject(page);
|
const auth = new AuthPageObject(page);
|
||||||
|
const password = 'testingpassword';
|
||||||
|
const email = auth.createRandomEmail();
|
||||||
|
|
||||||
|
// sign up
|
||||||
await page.goto('/auth/sign-up');
|
await page.goto('/auth/sign-up');
|
||||||
|
|
||||||
const email = `${(Math.random() * 1000000).toFixed(0)}@makerkit.dev`;
|
|
||||||
|
|
||||||
await auth.signUp({
|
await auth.signUp({
|
||||||
email,
|
email,
|
||||||
password: 'testingpassword',
|
password,
|
||||||
repeatPassword: 'testingpassword',
|
repeatPassword: password,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// confirm email
|
||||||
await auth.visitConfirmEmailLink(email);
|
await auth.visitConfirmEmailLink(email);
|
||||||
|
|
||||||
await page.goto('/home');
|
|
||||||
|
|
||||||
if (params.afterSignIn) {
|
if (params.afterSignIn) {
|
||||||
await params.afterSignIn();
|
await params.afterSignIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sign out
|
||||||
await auth.signOut();
|
await auth.signOut();
|
||||||
await page.waitForURL('/');
|
await page.waitForURL('/');
|
||||||
|
|
||||||
|
// return the email
|
||||||
return email;
|
return email;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export class AuthPageObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createRandomEmail() {
|
createRandomEmail() {
|
||||||
const value = Math.random() * 10000000000;
|
const value = Math.random() * 10000000000000;
|
||||||
|
|
||||||
return `${value.toFixed(0)}@makerkit.dev`;
|
return `${value.toFixed(0)}@makerkit.dev`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ test.describe('Password Reset Flow', () => {
|
|||||||
let email = '';
|
let email = '';
|
||||||
|
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
email = `test-${Math.random() * 10000}@makerkit.dev`;
|
email = auth.createRandomEmail();
|
||||||
|
|
||||||
await page.goto('/auth/sign-up');
|
await page.goto('/auth/sign-up');
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,45 @@
|
|||||||
import { Page } from '@playwright/test';
|
import { Page } from '@playwright/test';
|
||||||
import { parse } from 'node-html-parser';
|
import { parse } from 'node-html-parser';
|
||||||
|
|
||||||
|
type MessageSummary = {
|
||||||
|
ID: string;
|
||||||
|
MessageID: string;
|
||||||
|
Read: boolean;
|
||||||
|
From: {
|
||||||
|
Name: string;
|
||||||
|
Address: string;
|
||||||
|
};
|
||||||
|
To: Array<{
|
||||||
|
Name: string;
|
||||||
|
Address: string;
|
||||||
|
}>;
|
||||||
|
Cc: Array<any>;
|
||||||
|
Bcc: Array<any>;
|
||||||
|
ReplyTo: Array<any>;
|
||||||
|
Subject: string;
|
||||||
|
Created: string;
|
||||||
|
Tags: Array<any>;
|
||||||
|
Size: number;
|
||||||
|
Attachments: number;
|
||||||
|
Snippet: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type MessagesResponse = {
|
||||||
|
total: number;
|
||||||
|
unread: number;
|
||||||
|
count: number;
|
||||||
|
messages_count: number;
|
||||||
|
start: number;
|
||||||
|
tags: Array<any>;
|
||||||
|
messages: MessageSummary[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mailbox class for interacting with the Mailpit mailbox API.
|
||||||
|
*/
|
||||||
export class Mailbox {
|
export class Mailbox {
|
||||||
|
static URL = 'http://127.0.0.1:54324';
|
||||||
|
|
||||||
constructor(private readonly page: Page) {}
|
constructor(private readonly page: Page) {}
|
||||||
|
|
||||||
async visitMailbox(
|
async visitMailbox(
|
||||||
@@ -11,28 +49,32 @@ export class Mailbox {
|
|||||||
subject?: string;
|
subject?: string;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const mailbox = email.split('@')[0];
|
|
||||||
|
|
||||||
console.log(`Visiting mailbox ${email} ...`);
|
console.log(`Visiting mailbox ${email} ...`);
|
||||||
|
|
||||||
if (!mailbox) {
|
if (!email) {
|
||||||
throw new Error('Invalid email');
|
throw new Error('Invalid email');
|
||||||
}
|
}
|
||||||
|
|
||||||
const json = await this.getEmail(mailbox, params);
|
const json = await this.getEmail(email, params);
|
||||||
|
|
||||||
if (!json?.body) {
|
if (!json) {
|
||||||
throw new Error('Email body was not found');
|
throw new Error('Email body was not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Email found for ${email}`, {
|
console.log(`Email found for email: ${email}`, {
|
||||||
id: json.id,
|
expectedEmail: email,
|
||||||
subject: json.subject,
|
id: json.ID,
|
||||||
date: json.date,
|
subject: json.Subject,
|
||||||
|
date: json.Date,
|
||||||
|
to: json.To[0],
|
||||||
|
text: json.Text,
|
||||||
});
|
});
|
||||||
|
|
||||||
const html = (json.body as { html: string }).html;
|
if (email !== json.To[0]!.Address) {
|
||||||
const el = parse(html);
|
throw new Error(`Email address mismatch. Expected ${email}, got ${json.To[0]!.Address}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const el = parse(json.HTML);
|
||||||
|
|
||||||
const linkHref = el.querySelector('a')?.getAttribute('href');
|
const linkHref = el.querySelector('a')?.getAttribute('href');
|
||||||
|
|
||||||
@@ -51,27 +93,27 @@ export class Mailbox {
|
|||||||
* @param deleteAfter Whether to delete the email after retrieving the OTP
|
* @param deleteAfter Whether to delete the email after retrieving the OTP
|
||||||
* @returns The OTP code
|
* @returns The OTP code
|
||||||
*/
|
*/
|
||||||
async getOtpFromEmail(email: string, deleteAfter: boolean = true) {
|
async getOtpFromEmail(email: string, deleteAfter = false) {
|
||||||
const mailbox = email.split('@')[0];
|
|
||||||
|
|
||||||
console.log(`Retrieving OTP from mailbox ${email} ...`);
|
console.log(`Retrieving OTP from mailbox ${email} ...`);
|
||||||
|
|
||||||
if (!mailbox) {
|
if (!email) {
|
||||||
throw new Error('Invalid email');
|
throw new Error('Invalid email');
|
||||||
}
|
}
|
||||||
|
|
||||||
const json = await this.getEmail(mailbox, {
|
const json = await this.getEmail(email, {
|
||||||
deleteAfter,
|
deleteAfter,
|
||||||
subject: `One-time password for Makerkit`,
|
subject: `One-time password for Makerkit`,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!json?.body) {
|
if (!json) {
|
||||||
throw new Error('Email body was not found');
|
throw new Error('Email body was not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const html = (json.body as { html: string }).html;
|
if (email !== json.To[0]!.Address) {
|
||||||
|
throw new Error(`Email address mismatch. Expected ${email}, got ${json.To[0]!.Address}`);
|
||||||
|
}
|
||||||
|
|
||||||
const text = html.match(
|
const text = json.HTML.match(
|
||||||
new RegExp(`Your one-time password is: (\\d{6})`),
|
new RegExp(`Your one-time password is: (\\d{6})`),
|
||||||
)?.[1];
|
)?.[1];
|
||||||
|
|
||||||
@@ -84,49 +126,66 @@ export class Mailbox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getEmail(
|
async getEmail(
|
||||||
mailbox: string,
|
email: string,
|
||||||
params: {
|
params: {
|
||||||
deleteAfter: boolean;
|
deleteAfter: boolean;
|
||||||
subject?: string;
|
subject?: string;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const url = `http://127.0.0.1:54324/api/v1/mailbox/${mailbox}`;
|
console.log(`Retrieving email from mailbox ${email}...`);
|
||||||
|
|
||||||
|
const url = `${Mailbox.URL}/api/v1/search?query=to:${email}`;
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Failed to fetch emails: ${response.statusText}`);
|
throw new Error(`Failed to fetch emails: ${response.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const json = (await response.json()) as Array<{
|
const messagesResponse = (await response.json()) as MessagesResponse;
|
||||||
id: string;
|
|
||||||
subject: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
if (!json || !json.length) {
|
if (!messagesResponse || !messagesResponse.messages?.length) {
|
||||||
console.log(`No emails found for mailbox ${mailbox}`);
|
console.log(`No emails found for mailbox ${email}`);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = params.subject
|
const message = params.subject
|
||||||
? (() => {
|
? (() => {
|
||||||
const filtered = json.filter(
|
const filtered = messagesResponse.messages.filter(
|
||||||
(item) => item.subject === params.subject,
|
(item) => item.Subject === params.subject,
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`Found ${filtered.length} emails with subject ${params.subject}`,
|
`Found ${filtered.length} emails with subject ${params.subject}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return filtered[filtered.length - 1];
|
// retrieve the latest by timestamp
|
||||||
|
return filtered.reduce((acc, item) => {
|
||||||
|
if (
|
||||||
|
new Date(acc.Created).getTime() < new Date(item.Created).getTime()
|
||||||
|
) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
});
|
||||||
})()
|
})()
|
||||||
: json[0];
|
: messagesResponse.messages.reduce((acc, item) => {
|
||||||
|
if (
|
||||||
|
new Date(acc.Created).getTime() < new Date(item.Created).getTime()
|
||||||
|
) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`Message: ${JSON.stringify(message)}`);
|
return acc;
|
||||||
|
});
|
||||||
|
|
||||||
const messageId = message?.id;
|
if (!message) {
|
||||||
const messageUrl = `${url}/${messageId}`;
|
throw new Error('No message found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageId = message.ID;
|
||||||
|
const messageUrl = `${Mailbox.URL}/api/v1/message/${messageId}`;
|
||||||
|
|
||||||
const messageResponse = await fetch(messageUrl);
|
const messageResponse = await fetch(messageUrl);
|
||||||
|
|
||||||
@@ -138,8 +197,9 @@ export class Mailbox {
|
|||||||
if (params.deleteAfter) {
|
if (params.deleteAfter) {
|
||||||
console.log(`Deleting email ${messageId} ...`);
|
console.log(`Deleting email ${messageId} ...`);
|
||||||
|
|
||||||
const res = await fetch(messageUrl, {
|
const res = await fetch(`${Mailbox.URL}/api/v1/messages`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
body: JSON.stringify({ Ids: [messageId] }),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
@@ -147,6 +207,35 @@ export class Mailbox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await messageResponse.json();
|
return (await messageResponse.json()) as Promise<{
|
||||||
|
ID: string;
|
||||||
|
MessageID: string;
|
||||||
|
From: {
|
||||||
|
Name: string;
|
||||||
|
Address: string;
|
||||||
|
};
|
||||||
|
To: Array<{
|
||||||
|
Name: string;
|
||||||
|
Address: string;
|
||||||
|
}>;
|
||||||
|
Cc: Array<any>;
|
||||||
|
Bcc: Array<any>;
|
||||||
|
ReplyTo: Array<any>;
|
||||||
|
ReturnPath: string;
|
||||||
|
Subject: string;
|
||||||
|
ListUnsubscribe: {
|
||||||
|
Header: string;
|
||||||
|
Links: Array<any>;
|
||||||
|
Errors: string;
|
||||||
|
HeaderPost: string;
|
||||||
|
};
|
||||||
|
Date: string;
|
||||||
|
Tags: Array<any>;
|
||||||
|
Text: string;
|
||||||
|
HTML: string;
|
||||||
|
Size: number;
|
||||||
|
Inline: Array<any>;
|
||||||
|
Attachments: Array<any>;
|
||||||
|
}>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhY
|
|||||||
SUPABASE_DB_WEBHOOK_SECRET=WEBHOOKSECRET
|
SUPABASE_DB_WEBHOOK_SECRET=WEBHOOKSECRET
|
||||||
|
|
||||||
# EMAILS
|
# EMAILS
|
||||||
EMAIL_SENDER=test@makerkit.dev
|
EMAIL_SENDER="Makerkit <admin@makerkit.dev>"
|
||||||
EMAIL_PORT=54325
|
EMAIL_PORT=54325
|
||||||
EMAIL_HOST=localhost
|
EMAIL_HOST=localhost
|
||||||
EMAIL_TLS=false
|
EMAIL_TLS=false
|
||||||
|
|||||||
@@ -57,13 +57,13 @@
|
|||||||
"@marsidev/react-turnstile": "^1.1.0",
|
"@marsidev/react-turnstile": "^1.1.0",
|
||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@supabase/supabase-js": "2.49.1",
|
"@supabase/supabase-js": "2.49.1",
|
||||||
"@tanstack/react-query": "5.67.1",
|
"@tanstack/react-query": "5.67.3",
|
||||||
"@tanstack/react-table": "^8.21.2",
|
"@tanstack/react-table": "^8.21.2",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"lucide-react": "^0.477.0",
|
"lucide-react": "^0.479.0",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"next-sitemap": "^4.2.3",
|
"next-sitemap": "^4.2.3",
|
||||||
"next-themes": "0.4.4",
|
"next-themes": "0.4.6",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
@@ -77,18 +77,19 @@
|
|||||||
"@kit/eslint-config": "workspace:*",
|
"@kit/eslint-config": "workspace:*",
|
||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@next/bundle-analyzer": "15.2.1",
|
"@next/bundle-analyzer": "15.2.2",
|
||||||
"@tailwindcss/postcss": "^4.0.9",
|
"@tailwindcss/postcss": "^4.0.13",
|
||||||
"@types/node": "^22.13.4",
|
"@types/node": "^22.13.4",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"@types/react-dom": "19.0.4",
|
"@types/react-dom": "19.0.4",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"babel-plugin-react-compiler": "19.0.0-beta-e1e972c-20250221",
|
"babel-plugin-react-compiler": "19.0.0-beta-e1e972c-20250221",
|
||||||
|
"cssnano": "^7.0.6",
|
||||||
"dotenv-cli": "^8.0.0",
|
"dotenv-cli": "^8.0.0",
|
||||||
"pino-pretty": "^13.0.0",
|
"pino-pretty": "^13.0.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"supabase": "^2.15.8",
|
"supabase": "^2.19.5",
|
||||||
"tailwindcss": "4.0.9",
|
"tailwindcss": "4.0.13",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"typescript": "^5.8.2"
|
"typescript": "^5.8.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export default {
|
export default {
|
||||||
plugins: {
|
plugins: {
|
||||||
'@tailwindcss/postcss': {},
|
'@tailwindcss/postcss': {},
|
||||||
|
...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -76,6 +76,9 @@ redirect_uri = ""
|
|||||||
# or any other third-party OIDC providers.
|
# or any other third-party OIDC providers.
|
||||||
url = ""
|
url = ""
|
||||||
|
|
||||||
|
[auth.rate_limit]
|
||||||
|
email_sent = 1000
|
||||||
|
|
||||||
[auth.email.template.invite]
|
[auth.email.template.invite]
|
||||||
subject = "You are invited to Makerkit"
|
subject = "You are invited to Makerkit"
|
||||||
content_path = "./supabase/templates/invite-user.html"
|
content_path = "./supabase/templates/invite-user.html"
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
"@supabase/supabase-js": "2.49.1",
|
"@supabase/supabase-js": "2.49.1",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"lucide-react": "^0.477.0",
|
"lucide-react": "^0.479.0",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
"react-i18next": "^15.4.1",
|
"react-i18next": "^15.4.1",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
"./components": "./src/components/index.ts"
|
"./components": "./src/components/index.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@stripe/react-stripe-js": "^3.3.0",
|
"@stripe/react-stripe-js": "^3.4.0",
|
||||||
"@stripe/stripe-js": "^5.8.0",
|
"@stripe/stripe-js": "^6.0.0",
|
||||||
"stripe": "^17.7.0"
|
"stripe": "^17.7.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"./api": "./src/server/api.ts"
|
"./api": "./src/server/api.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^5.1.2"
|
"nanoid": "^5.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@hookform/resolvers": "^4.1.3",
|
"@hookform/resolvers": "^4.1.3",
|
||||||
@@ -35,12 +35,12 @@
|
|||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@supabase/supabase-js": "2.49.1",
|
"@supabase/supabase-js": "2.49.1",
|
||||||
"@tanstack/react-query": "5.67.1",
|
"@tanstack/react-query": "5.67.3",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"@types/react-dom": "19.0.4",
|
"@types/react-dom": "19.0.4",
|
||||||
"lucide-react": "^0.477.0",
|
"lucide-react": "^0.479.0",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"next-themes": "0.4.4",
|
"next-themes": "0.4.6",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
|
|||||||
@@ -21,11 +21,11 @@
|
|||||||
"@makerkit/data-loader-supabase-core": "^0.0.8",
|
"@makerkit/data-loader-supabase-core": "^0.0.8",
|
||||||
"@makerkit/data-loader-supabase-nextjs": "^1.2.3",
|
"@makerkit/data-loader-supabase-nextjs": "^1.2.3",
|
||||||
"@supabase/supabase-js": "2.49.1",
|
"@supabase/supabase-js": "2.49.1",
|
||||||
"@tanstack/react-query": "5.67.1",
|
"@tanstack/react-query": "5.67.3",
|
||||||
"@tanstack/react-table": "^8.21.2",
|
"@tanstack/react-table": "^8.21.2",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"lucide-react": "^0.477.0",
|
"lucide-react": "^0.479.0",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
|
|||||||
@@ -29,10 +29,10 @@
|
|||||||
"@marsidev/react-turnstile": "^1.1.0",
|
"@marsidev/react-turnstile": "^1.1.0",
|
||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@supabase/supabase-js": "2.49.1",
|
"@supabase/supabase-js": "2.49.1",
|
||||||
"@tanstack/react-query": "5.67.1",
|
"@tanstack/react-query": "5.67.3",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"lucide-react": "^0.477.0",
|
"lucide-react": "^0.479.0",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
"react-i18next": "^15.4.1",
|
"react-i18next": "^15.4.1",
|
||||||
"sonner": "^2.0.1",
|
"sonner": "^2.0.1",
|
||||||
|
|||||||
@@ -20,9 +20,9 @@
|
|||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@supabase/supabase-js": "2.49.1",
|
"@supabase/supabase-js": "2.49.1",
|
||||||
"@tanstack/react-query": "5.67.1",
|
"@tanstack/react-query": "5.67.3",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"lucide-react": "^0.477.0",
|
"lucide-react": "^0.479.0",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-i18next": "^15.4.1"
|
"react-i18next": "^15.4.1"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
"./webhooks": "./src/server/services/webhooks/index.ts"
|
"./webhooks": "./src/server/services/webhooks/index.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^5.1.2"
|
"nanoid": "^5.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@hookform/resolvers": "^4.1.3",
|
"@hookform/resolvers": "^4.1.3",
|
||||||
@@ -33,14 +33,14 @@
|
|||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
"@supabase/supabase-js": "2.49.1",
|
"@supabase/supabase-js": "2.49.1",
|
||||||
"@tanstack/react-query": "5.67.1",
|
"@tanstack/react-query": "5.67.3",
|
||||||
"@tanstack/react-table": "^8.21.2",
|
"@tanstack/react-table": "^8.21.2",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"@types/react-dom": "19.0.4",
|
"@types/react-dom": "19.0.4",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"lucide-react": "^0.477.0",
|
"lucide-react": "^0.479.0",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
|
|||||||
@@ -8,16 +8,33 @@ import { Database } from '@kit/supabase/database';
|
|||||||
type Invitation = Database['public']['Tables']['invitations']['Row'];
|
type Invitation = Database['public']['Tables']['invitations']['Row'];
|
||||||
|
|
||||||
const invitePath = '/join';
|
const invitePath = '/join';
|
||||||
|
|
||||||
const siteURL = process.env.NEXT_PUBLIC_SITE_URL;
|
const siteURL = process.env.NEXT_PUBLIC_SITE_URL;
|
||||||
const productName = process.env.NEXT_PUBLIC_PRODUCT_NAME ?? '';
|
const productName = process.env.NEXT_PUBLIC_PRODUCT_NAME ?? '';
|
||||||
const emailSender = process.env.EMAIL_SENDER;
|
const emailSender = process.env.EMAIL_SENDER;
|
||||||
|
|
||||||
const env = z
|
const env = z
|
||||||
.object({
|
.object({
|
||||||
invitePath: z.string().min(1),
|
invitePath: z
|
||||||
siteURL: z.string().min(1),
|
.string({
|
||||||
productName: z.string(),
|
required_error: 'The property invitePath is required',
|
||||||
emailSender: z.string().email(),
|
})
|
||||||
|
.min(1),
|
||||||
|
siteURL: z
|
||||||
|
.string({
|
||||||
|
required_error: 'NEXT_PUBLIC_SITE_URL is required',
|
||||||
|
})
|
||||||
|
.min(1),
|
||||||
|
productName: z
|
||||||
|
.string({
|
||||||
|
required_error: 'NEXT_PUBLIC_PRODUCT_NAME is required',
|
||||||
|
})
|
||||||
|
.min(1),
|
||||||
|
emailSender: z
|
||||||
|
.string({
|
||||||
|
required_error: 'EMAIL_SENDER is required',
|
||||||
|
})
|
||||||
|
.min(1),
|
||||||
})
|
})
|
||||||
.parse({
|
.parse({
|
||||||
invitePath,
|
invitePath,
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ class AccountWebhooksService {
|
|||||||
return z
|
return z
|
||||||
.object({
|
.object({
|
||||||
productName: z.string(),
|
productName: z.string(),
|
||||||
fromEmail: z.string().email(),
|
fromEmail: z.string({
|
||||||
|
required_error: 'EMAIL_SENDER is required',
|
||||||
|
}).min(1),
|
||||||
})
|
})
|
||||||
.parse({
|
.parse({
|
||||||
productName,
|
productName,
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/shared": "workspace:*",
|
"@kit/shared": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@tanstack/react-query": "5.67.1",
|
"@tanstack/react-query": "5.67.3",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-i18next": "^15.4.1"
|
"react-i18next": "^15.4.1"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"./config/server": "./src/sentry.client.server.ts"
|
"./config/server": "./src/sentry.client.server.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/nextjs": "^9.3.0",
|
"@sentry/nextjs": "^9.5.0",
|
||||||
"import-in-the-middle": "1.13.1"
|
"import-in-the-middle": "1.13.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -39,18 +39,25 @@ export class SentryMonitoringService implements MonitoringService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async initialize() {
|
private async initialize() {
|
||||||
|
const environment =
|
||||||
|
process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT ?? process.env.VERCEL_ENV;
|
||||||
|
|
||||||
if (typeof document !== 'undefined') {
|
if (typeof document !== 'undefined') {
|
||||||
const { initializeSentryBrowserClient } = await import(
|
const { initializeSentryBrowserClient } = await import(
|
||||||
'../sentry.client.config'
|
'../sentry.client.config'
|
||||||
);
|
);
|
||||||
|
|
||||||
initializeSentryBrowserClient();
|
initializeSentryBrowserClient({
|
||||||
|
environment,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const { initializeSentryServerClient } = await import(
|
const { initializeSentryServerClient } = await import(
|
||||||
'../sentry.server.config'
|
'../sentry.server.config'
|
||||||
);
|
);
|
||||||
|
|
||||||
initializeSentryServerClient();
|
initializeSentryServerClient({
|
||||||
|
environment,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.readyResolver?.();
|
this.readyResolver?.();
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"@kit/supabase": "workspace:*",
|
"@kit/supabase": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@supabase/supabase-js": "2.49.1",
|
"@supabase/supabase-js": "2.49.1",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
|
|||||||
@@ -29,9 +29,9 @@
|
|||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@supabase/ssr": "^0.5.2",
|
"@supabase/ssr": "^0.5.2",
|
||||||
"@supabase/supabase-js": "2.49.1",
|
"@supabase/supabase-js": "2.49.1",
|
||||||
"@tanstack/react-query": "5.67.1",
|
"@tanstack/react-query": "5.67.3",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"server-only": "^0.0.1",
|
"server-only": "^0.0.1",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "1.0.4",
|
"cmdk": "1.0.4",
|
||||||
"input-otp": "1.4.2",
|
"input-otp": "1.4.2",
|
||||||
"lucide-react": "^0.477.0",
|
"lucide-react": "^0.479.0",
|
||||||
"react-top-loading-bar": "3.0.2",
|
"react-top-loading-bar": "3.0.2",
|
||||||
"recharts": "2.15.1",
|
"recharts": "2.15.1",
|
||||||
"tailwind-merge": "^3.0.2"
|
"tailwind-merge": "^3.0.2"
|
||||||
@@ -43,21 +43,21 @@
|
|||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@tanstack/react-query": "5.67.1",
|
"@tanstack/react-query": "5.67.3",
|
||||||
"@tanstack/react-table": "^8.21.2",
|
"@tanstack/react-table": "^8.21.2",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"@types/react-dom": "19.0.4",
|
"@types/react-dom": "19.0.4",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.22.0",
|
||||||
"next": "15.2.1",
|
"next": "15.2.2",
|
||||||
"next-themes": "0.4.4",
|
"next-themes": "0.4.6",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"react-day-picker": "^8.10.1",
|
"react-day-picker": "^8.10.1",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
"react-i18next": "^15.4.1",
|
"react-i18next": "^15.4.1",
|
||||||
"sonner": "^2.0.1",
|
"sonner": "^2.0.1",
|
||||||
"tailwindcss": "4.0.9",
|
"tailwindcss": "4.0.13",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { GradientSecondaryText } from './gradient-secondary-text';
|
|||||||
|
|
||||||
export const Pill: React.FC<
|
export const Pill: React.FC<
|
||||||
React.HTMLAttributes<HTMLHeadingElement> & {
|
React.HTMLAttributes<HTMLHeadingElement> & {
|
||||||
label?: string;
|
label?: React.ReactNode;
|
||||||
asChild?: boolean;
|
asChild?: boolean;
|
||||||
}
|
}
|
||||||
> = function PillComponent({ className, asChild, ...props }) {
|
> = function PillComponent({ className, asChild, ...props }) {
|
||||||
|
|||||||
2055
pnpm-lock.yaml
generated
2055
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -13,16 +13,16 @@
|
|||||||
"format": "prettier --check \"**/*.{js,json}\""
|
"format": "prettier --check \"**/*.{js,json}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next/eslint-plugin-next": "15.2.1",
|
"@next/eslint-plugin-next": "15.2.2",
|
||||||
"@types/eslint": "9.6.1",
|
"@types/eslint": "9.6.1",
|
||||||
"eslint-config-next": "15.2.1",
|
"eslint-config-next": "15.2.2",
|
||||||
"eslint-config-turbo": "^2.4.4",
|
"eslint-config-turbo": "^2.4.4",
|
||||||
"typescript-eslint": "8.26.0"
|
"typescript-eslint": "8.26.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.22.0",
|
||||||
"typescript": "^5.8.2"
|
"typescript": "^5.8.2"
|
||||||
},
|
},
|
||||||
"prettier": "@kit/prettier-config"
|
"prettier": "@kit/prettier-config"
|
||||||
|
|||||||
Reference in New Issue
Block a user