refactor: replace createSafeActionClient with userActionClient for improved session handling across multiple actions
This commit is contained in:
parent
262228d6e9
commit
d1928575b3
@ -1,19 +1,16 @@
|
||||
'use server';
|
||||
|
||||
import { userActionClient } from '@/lib/safe-action';
|
||||
import { isSubscribed } from '@/newsletter';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
// Newsletter schema for validation
|
||||
const newsletterSchema = z.object({
|
||||
email: z.email({ error: 'Please enter a valid email address' }),
|
||||
});
|
||||
|
||||
// Create a safe action to check if a user is subscribed to the newsletter
|
||||
export const checkNewsletterStatusAction = actionClient
|
||||
export const checkNewsletterStatusAction = userActionClient
|
||||
.schema(newsletterSchema)
|
||||
.action(async ({ parsedInput: { email } }) => {
|
||||
try {
|
||||
|
@ -2,29 +2,21 @@
|
||||
|
||||
import { getWebContentAnalysisCost } from '@/ai/text/utils/web-content-analyzer-config';
|
||||
import { getUserCredits, hasEnoughCredits } from '@/credits/credits';
|
||||
import { getSession } from '@/lib/server';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
|
||||
const actionClient = createSafeActionClient();
|
||||
import type { User } from '@/lib/auth-types';
|
||||
import { userActionClient } from '@/lib/safe-action';
|
||||
|
||||
/**
|
||||
* Check if user has enough credits for web content analysis
|
||||
*/
|
||||
export const checkWebContentAnalysisCreditsAction = actionClient.action(
|
||||
async () => {
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
console.warn(
|
||||
'unauthorized request to check web content analysis credits'
|
||||
);
|
||||
return { success: false, error: 'Unauthorized' };
|
||||
}
|
||||
export const checkWebContentAnalysisCreditsAction = userActionClient.action(
|
||||
async ({ ctx }) => {
|
||||
const currentUser = (ctx as { user: User }).user;
|
||||
|
||||
try {
|
||||
const requiredCredits = getWebContentAnalysisCost();
|
||||
const currentCredits = await getUserCredits(session.user.id);
|
||||
const currentCredits = await getUserCredits(currentUser.id);
|
||||
const hasCredits = await hasEnoughCredits({
|
||||
userId: session.user.id,
|
||||
userId: currentUser.id,
|
||||
requiredCredits,
|
||||
});
|
||||
|
||||
|
@ -1,12 +1,10 @@
|
||||
'use server';
|
||||
|
||||
import { consumeCredits } from '@/credits/credits';
|
||||
import { getSession } from '@/lib/server';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import type { User } from '@/lib/auth-types';
|
||||
import { userActionClient } from '@/lib/safe-action';
|
||||
import { z } from 'zod';
|
||||
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
// consume credits schema
|
||||
const consumeSchema = z.object({
|
||||
amount: z.number().min(1),
|
||||
@ -16,21 +14,17 @@ const consumeSchema = z.object({
|
||||
/**
|
||||
* Consume credits
|
||||
*/
|
||||
export const consumeCreditsAction = actionClient
|
||||
export const consumeCreditsAction = userActionClient
|
||||
.schema(consumeSchema)
|
||||
.action(async ({ parsedInput }) => {
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
console.warn('unauthorized request to consume credits');
|
||||
return { success: false, error: 'Unauthorized' };
|
||||
}
|
||||
.action(async ({ parsedInput, ctx }) => {
|
||||
const { amount, description } = parsedInput;
|
||||
const currentUser = (ctx as { user: User }).user;
|
||||
|
||||
try {
|
||||
await consumeCredits({
|
||||
userId: session.user.id,
|
||||
amount: parsedInput.amount,
|
||||
description:
|
||||
parsedInput.description || `Consume credits: ${parsedInput.amount}`,
|
||||
userId: currentUser.id,
|
||||
amount,
|
||||
description: description || `Consume credits: ${amount}`,
|
||||
});
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
|
@ -1,20 +1,17 @@
|
||||
'use server';
|
||||
|
||||
import { websiteConfig } from '@/config/website';
|
||||
import type { User } from '@/lib/auth-types';
|
||||
import { findPlanByPlanId } from '@/lib/price-plan';
|
||||
import { getSession } from '@/lib/server';
|
||||
import { userActionClient } from '@/lib/safe-action';
|
||||
import { getUrlWithLocale } from '@/lib/urls/urls';
|
||||
import { createCheckout } from '@/payment';
|
||||
import type { CreateCheckoutParams } from '@/payment/types';
|
||||
import { Routes } from '@/routes';
|
||||
import { getLocale } from 'next-intl/server';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { cookies } from 'next/headers';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
// Checkout schema for validation
|
||||
// metadata is optional, and may contain referral information if you need
|
||||
const checkoutSchema = z.object({
|
||||
@ -27,33 +24,11 @@ const checkoutSchema = z.object({
|
||||
/**
|
||||
* Create a checkout session for a price plan
|
||||
*/
|
||||
export const createCheckoutAction = actionClient
|
||||
export const createCheckoutAction = userActionClient
|
||||
.schema(checkoutSchema)
|
||||
.action(async ({ parsedInput }) => {
|
||||
const { userId, planId, priceId, metadata } = parsedInput;
|
||||
|
||||
// Get the current user session for authorization
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
console.warn(
|
||||
`unauthorized request to create checkout session for user ${userId}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
};
|
||||
}
|
||||
|
||||
// Only allow users to create their own checkout session
|
||||
if (session.user.id !== userId) {
|
||||
console.warn(
|
||||
`current user ${session.user.id} is not authorized to create checkout session for user ${userId}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Not authorized to do this action',
|
||||
};
|
||||
}
|
||||
.action(async ({ parsedInput, ctx }) => {
|
||||
const { planId, priceId, metadata } = parsedInput;
|
||||
const currentUser = (ctx as { user: User }).user;
|
||||
|
||||
try {
|
||||
// Get the current locale from the request
|
||||
@ -71,8 +46,8 @@ export const createCheckoutAction = actionClient
|
||||
// Add user id to metadata, so we can get it in the webhook event
|
||||
const customMetadata: Record<string, string> = {
|
||||
...metadata,
|
||||
userId: session.user.id,
|
||||
userName: session.user.name,
|
||||
userId: currentUser.id,
|
||||
userName: currentUser.name,
|
||||
};
|
||||
|
||||
// https://datafa.st/docs/stripe-checkout-api
|
||||
@ -94,7 +69,7 @@ export const createCheckoutAction = actionClient
|
||||
const params: CreateCheckoutParams = {
|
||||
planId,
|
||||
priceId,
|
||||
customerEmail: session.user.email,
|
||||
customerEmail: currentUser.email,
|
||||
metadata: customMetadata,
|
||||
successUrl,
|
||||
cancelUrl,
|
||||
|
@ -2,19 +2,16 @@
|
||||
|
||||
import { websiteConfig } from '@/config/website';
|
||||
import { getCreditPackageById } from '@/credits/server';
|
||||
import { getSession } from '@/lib/server';
|
||||
import type { User } from '@/lib/auth-types';
|
||||
import { userActionClient } from '@/lib/safe-action';
|
||||
import { getUrlWithLocale } from '@/lib/urls/urls';
|
||||
import { createCreditCheckout } from '@/payment';
|
||||
import type { CreateCreditCheckoutParams } from '@/payment/types';
|
||||
import { Routes } from '@/routes';
|
||||
import { getLocale } from 'next-intl/server';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { cookies } from 'next/headers';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
// Credit checkout schema for validation
|
||||
// metadata is optional, and may contain referral information if you need
|
||||
const creditCheckoutSchema = z.object({
|
||||
@ -27,33 +24,11 @@ const creditCheckoutSchema = z.object({
|
||||
/**
|
||||
* Create a checkout session for a credit package
|
||||
*/
|
||||
export const createCreditCheckoutSession = actionClient
|
||||
export const createCreditCheckoutSession = userActionClient
|
||||
.schema(creditCheckoutSchema)
|
||||
.action(async ({ parsedInput }) => {
|
||||
const { userId, packageId, priceId, metadata } = parsedInput;
|
||||
|
||||
// Get the current user session for authorization
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
console.warn(
|
||||
`unauthorized request to create credit checkout session for user ${userId}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
};
|
||||
}
|
||||
|
||||
// Only allow users to create their own checkout session
|
||||
if (session.user.id !== userId) {
|
||||
console.warn(
|
||||
`current user ${session.user.id} is not authorized to create credit checkout session for user ${userId}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Not authorized to do this action',
|
||||
};
|
||||
}
|
||||
.action(async ({ parsedInput, ctx }) => {
|
||||
const { packageId, priceId, metadata } = parsedInput;
|
||||
const currentUser = (ctx as { user: User }).user;
|
||||
|
||||
try {
|
||||
// Get the current locale from the request
|
||||
@ -74,8 +49,8 @@ export const createCreditCheckoutSession = actionClient
|
||||
type: 'credit_purchase',
|
||||
packageId,
|
||||
credits: creditPackage.credits.toString(),
|
||||
userId: session.user.id,
|
||||
userName: session.user.name,
|
||||
userId: currentUser.id,
|
||||
userName: currentUser.name,
|
||||
};
|
||||
|
||||
// https://datafa.st/docs/stripe-checkout-api
|
||||
@ -98,7 +73,7 @@ export const createCreditCheckoutSession = actionClient
|
||||
const params: CreateCreditCheckoutParams = {
|
||||
packageId,
|
||||
priceId,
|
||||
customerEmail: session.user.email,
|
||||
customerEmail: currentUser.email,
|
||||
metadata: customMetadata,
|
||||
successUrl,
|
||||
cancelUrl,
|
||||
|
@ -2,18 +2,15 @@
|
||||
|
||||
import { getDb } from '@/db';
|
||||
import { user } from '@/db/schema';
|
||||
import { getSession } from '@/lib/server';
|
||||
import type { User } from '@/lib/auth-types';
|
||||
import { userActionClient } from '@/lib/safe-action';
|
||||
import { getUrlWithLocale } from '@/lib/urls/urls';
|
||||
import { createCustomerPortal } from '@/payment';
|
||||
import type { CreatePortalParams } from '@/payment/types';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { getLocale } from 'next-intl/server';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
// Portal schema for validation
|
||||
const portalSchema = z.object({
|
||||
userId: z.string().min(1, { error: 'User ID is required' }),
|
||||
@ -26,33 +23,11 @@ const portalSchema = z.object({
|
||||
/**
|
||||
* Create a customer portal session
|
||||
*/
|
||||
export const createPortalAction = actionClient
|
||||
export const createPortalAction = userActionClient
|
||||
.schema(portalSchema)
|
||||
.action(async ({ parsedInput }) => {
|
||||
const { userId, returnUrl } = parsedInput;
|
||||
|
||||
// Get the current user session for authorization
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
console.warn(
|
||||
`unauthorized request to create portal session for user ${userId}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
};
|
||||
}
|
||||
|
||||
// Only allow users to create their own portal session
|
||||
if (session.user.id !== userId) {
|
||||
console.warn(
|
||||
`current user ${session.user.id} is not authorized to create portal session for user ${userId}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Not authorized to do this action',
|
||||
};
|
||||
}
|
||||
.action(async ({ parsedInput, ctx }) => {
|
||||
const { returnUrl } = parsedInput;
|
||||
const currentUser = (ctx as { user: User }).user;
|
||||
|
||||
try {
|
||||
// Get the user's customer ID from the database
|
||||
@ -60,11 +35,11 @@ export const createPortalAction = actionClient
|
||||
const customerResult = await db
|
||||
.select({ customerId: user.customerId })
|
||||
.from(user)
|
||||
.where(eq(user.id, session.user.id))
|
||||
.where(eq(user.id, currentUser.id))
|
||||
.limit(1);
|
||||
|
||||
if (customerResult.length <= 0 || !customerResult[0].customerId) {
|
||||
console.error(`No customer found for user ${session.user.id}`);
|
||||
console.error(`No customer found for user ${currentUser.id}`);
|
||||
return {
|
||||
success: false,
|
||||
error: 'No customer found for user',
|
||||
|
@ -1,13 +1,10 @@
|
||||
'use server';
|
||||
|
||||
import { getSession } from '@/lib/server';
|
||||
import type { User } from '@/lib/auth-types';
|
||||
import { userActionClient } from '@/lib/safe-action';
|
||||
import { getSubscriptions } from '@/payment';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
// Input schema
|
||||
const schema = z.object({
|
||||
userId: z.string().min(1, { error: 'User ID is required' }),
|
||||
@ -19,33 +16,10 @@ const schema = z.object({
|
||||
* If the user has multiple subscriptions,
|
||||
* it returns the most recent active or trialing one
|
||||
*/
|
||||
export const getActiveSubscriptionAction = actionClient
|
||||
export const getActiveSubscriptionAction = userActionClient
|
||||
.schema(schema)
|
||||
.action(async ({ parsedInput }) => {
|
||||
const { userId } = parsedInput;
|
||||
|
||||
// Get the current user session for authorization
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
console.warn(
|
||||
`unauthorized request to get active subscription for user ${userId}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
};
|
||||
}
|
||||
|
||||
// Only allow users to check their own status unless they're admins
|
||||
if (session.user.id !== userId && session.user.role !== 'admin') {
|
||||
console.warn(
|
||||
`current user ${session.user.id} is not authorized to get active subscription for user ${userId}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Not authorized to do this action',
|
||||
};
|
||||
}
|
||||
.action(async ({ ctx }) => {
|
||||
const currentUser = (ctx as { user: User }).user;
|
||||
|
||||
// Check if Stripe environment variables are configured
|
||||
const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
|
||||
@ -62,7 +36,7 @@ export const getActiveSubscriptionAction = actionClient
|
||||
try {
|
||||
// Find the user's most recent active subscription
|
||||
const subscriptions = await getSubscriptions({
|
||||
userId: session.user.id,
|
||||
userId: currentUser.id,
|
||||
});
|
||||
// console.log('get user subscriptions:', subscriptions);
|
||||
|
||||
@ -76,16 +50,16 @@ export const getActiveSubscriptionAction = actionClient
|
||||
|
||||
// If found, use it
|
||||
if (activeSubscription) {
|
||||
console.log('find active subscription for userId:', session.user.id);
|
||||
console.log('find active subscription for userId:', currentUser.id);
|
||||
subscriptionData = activeSubscription;
|
||||
} else {
|
||||
console.log(
|
||||
'no active subscription found for userId:',
|
||||
session.user.id
|
||||
currentUser.id
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.log('no subscriptions found for userId:', session.user.id);
|
||||
console.log('no subscriptions found for userId:', currentUser.id);
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -1,21 +1,16 @@
|
||||
'use server';
|
||||
|
||||
import { getUserCredits } from '@/credits/credits';
|
||||
import { getSession } from '@/lib/server';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
|
||||
const actionClient = createSafeActionClient();
|
||||
import type { User } from '@/lib/auth-types';
|
||||
import { userActionClient } from '@/lib/safe-action';
|
||||
|
||||
/**
|
||||
* Get current user's credits
|
||||
*/
|
||||
export const getCreditBalanceAction = actionClient.action(async () => {
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
console.warn('unauthorized request to get credit balance');
|
||||
return { success: false, error: 'Unauthorized' };
|
||||
export const getCreditBalanceAction = userActionClient.action(
|
||||
async ({ ctx }) => {
|
||||
const user = (ctx as { user: User }).user;
|
||||
const credits = await getUserCredits(user.id);
|
||||
return { success: true, credits };
|
||||
}
|
||||
|
||||
const credits = await getUserCredits(session.user.id);
|
||||
return { success: true, credits };
|
||||
});
|
||||
);
|
||||
|
@ -3,34 +3,23 @@
|
||||
import { CREDIT_TRANSACTION_TYPE } from '@/credits/types';
|
||||
import { getDb } from '@/db';
|
||||
import { creditTransaction } from '@/db/schema';
|
||||
import { getSession } from '@/lib/server';
|
||||
import type { User } from '@/lib/auth-types';
|
||||
import { userActionClient } from '@/lib/safe-action';
|
||||
import { addDays } from 'date-fns';
|
||||
import { and, eq, gte, isNotNull, lte, sql, sum } from 'drizzle-orm';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
|
||||
const CREDITS_EXPIRATION_DAYS = 31;
|
||||
const CREDITS_MONTHLY_DAYS = 31;
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
/**
|
||||
* Get credit statistics for a user
|
||||
*/
|
||||
export const getCreditStatsAction = actionClient.action(async () => {
|
||||
export const getCreditStatsAction = userActionClient.action(async ({ ctx }) => {
|
||||
try {
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
console.warn('unauthorized request to get credit stats');
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
};
|
||||
}
|
||||
const currentUser = (ctx as { user: User }).user;
|
||||
const userId = currentUser.id;
|
||||
|
||||
const db = await getDb();
|
||||
const userId = session.user.id;
|
||||
|
||||
// Get credits expiring in the next CREDITS_EXPIRATION_DAYS days
|
||||
const expirationDaysFromNow = addDays(new Date(), CREDITS_EXPIRATION_DAYS);
|
||||
const expiringCredits = await db
|
||||
|
@ -2,14 +2,11 @@
|
||||
|
||||
import { getDb } from '@/db';
|
||||
import { creditTransaction } from '@/db/schema';
|
||||
import { getSession } from '@/lib/server';
|
||||
import type { User } from '@/lib/auth-types';
|
||||
import { userActionClient } from '@/lib/safe-action';
|
||||
import { and, asc, desc, eq, ilike, or, sql } from 'drizzle-orm';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
// Define the schema for getCreditTransactions parameters
|
||||
const getCreditTransactionsSchema = z.object({
|
||||
pageIndex: z.number().min(0).default(0),
|
||||
@ -40,23 +37,17 @@ const sortFieldMap = {
|
||||
} as const;
|
||||
|
||||
// Create a safe action for getting credit transactions
|
||||
export const getCreditTransactionsAction = actionClient
|
||||
export const getCreditTransactionsAction = userActionClient
|
||||
.schema(getCreditTransactionsSchema)
|
||||
.action(async ({ parsedInput }) => {
|
||||
.action(async ({ parsedInput, ctx }) => {
|
||||
try {
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
};
|
||||
}
|
||||
const { pageIndex, pageSize, search, sorting } = parsedInput;
|
||||
const currentUser = (ctx as { user: User }).user;
|
||||
|
||||
// search by type, amount, paymentId, description, and restrict to current user
|
||||
const where = search
|
||||
? and(
|
||||
eq(creditTransaction.userId, session.user.id),
|
||||
eq(creditTransaction.userId, currentUser.id),
|
||||
or(
|
||||
ilike(creditTransaction.type, `%${search}%`),
|
||||
ilike(creditTransaction.amount, `%${search}%`),
|
||||
@ -65,7 +56,7 @@ export const getCreditTransactionsAction = actionClient
|
||||
ilike(creditTransaction.description, `%${search}%`)
|
||||
)
|
||||
)
|
||||
: eq(creditTransaction.userId, session.user.id);
|
||||
: eq(creditTransaction.userId, currentUser.id);
|
||||
|
||||
const offset = pageIndex * pageSize;
|
||||
|
||||
|
@ -2,16 +2,13 @@
|
||||
|
||||
import { getDb } from '@/db';
|
||||
import { payment } from '@/db/schema';
|
||||
import type { User } from '@/lib/auth-types';
|
||||
import { findPlanByPriceId, getAllPricePlans } from '@/lib/price-plan';
|
||||
import { getSession } from '@/lib/server';
|
||||
import { userActionClient } from '@/lib/safe-action';
|
||||
import { PaymentTypes } from '@/payment/types';
|
||||
import { and, eq } from 'drizzle-orm';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
// Input schema
|
||||
const schema = z.object({
|
||||
userId: z.string().min(1, { error: 'User ID is required' }),
|
||||
@ -25,33 +22,11 @@ const schema = z.object({
|
||||
* in order to do this, you have to update the logic to check the lifetime status,
|
||||
* for example, just check the planId is `lifetime` or not.
|
||||
*/
|
||||
export const getLifetimeStatusAction = actionClient
|
||||
export const getLifetimeStatusAction = userActionClient
|
||||
.schema(schema)
|
||||
.action(async ({ parsedInput }) => {
|
||||
const { userId } = parsedInput;
|
||||
|
||||
// Get the current user session for authorization
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
console.warn(
|
||||
`unauthorized request to get lifetime status for user ${userId}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
};
|
||||
}
|
||||
|
||||
// Only allow users to check their own status unless they're admins
|
||||
if (session.user.id !== userId && session.user.role !== 'admin') {
|
||||
console.warn(
|
||||
`current user ${session.user.id} is not authorized to get lifetime status for user ${userId}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Not authorized to do this action',
|
||||
};
|
||||
}
|
||||
.action(async ({ ctx }) => {
|
||||
const currentUser = (ctx as { user: User }).user;
|
||||
const userId = currentUser.id;
|
||||
|
||||
try {
|
||||
// Get lifetime plans
|
||||
|
@ -3,14 +3,10 @@
|
||||
import { getDb } from '@/db';
|
||||
import { user } from '@/db/schema';
|
||||
import { isDemoWebsite } from '@/lib/demo';
|
||||
import { getSession } from '@/lib/server';
|
||||
import { adminActionClient } from '@/lib/safe-action';
|
||||
import { asc, desc, ilike, or, sql } from 'drizzle-orm';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
// Define the schema for getUsers parameters
|
||||
const getUsersSchema = z.object({
|
||||
pageIndex: z.number().min(0).default(0),
|
||||
@ -40,17 +36,9 @@ const sortFieldMap = {
|
||||
} as const;
|
||||
|
||||
// Create a safe action for getting users
|
||||
export const getUsersAction = actionClient
|
||||
export const getUsersAction = adminActionClient
|
||||
.schema(getUsersSchema)
|
||||
.action(async ({ parsedInput }) => {
|
||||
const session = await getSession();
|
||||
if (!session || session.user.role !== 'admin') {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const { pageIndex, pageSize, search, sorting } = parsedInput;
|
||||
|
||||
|
@ -1,14 +1,11 @@
|
||||
'use server';
|
||||
|
||||
import { websiteConfig } from '@/config/website';
|
||||
import { actionClient } from '@/lib/safe-action';
|
||||
import { sendEmail } from '@/mail';
|
||||
import { getLocale } from 'next-intl/server';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
/**
|
||||
* DOC: When using Zod for validation, how can I localize error messages?
|
||||
* https://next-intl.dev/docs/environments/actions-metadata-route-handlers#server-actions
|
||||
|
@ -1,14 +1,11 @@
|
||||
'use server';
|
||||
|
||||
import { actionClient } from '@/lib/safe-action';
|
||||
import { sendEmail } from '@/mail';
|
||||
import { subscribe } from '@/newsletter';
|
||||
import { getLocale } from 'next-intl/server';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
// Newsletter schema for validation
|
||||
const newsletterSchema = z.object({
|
||||
email: z.email({ error: 'Please enter a valid email address' }),
|
||||
|
@ -1,30 +1,18 @@
|
||||
'use server';
|
||||
|
||||
import { getSession } from '@/lib/server';
|
||||
import { userActionClient } from '@/lib/safe-action';
|
||||
import { unsubscribe } from '@/newsletter';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
// Newsletter schema for validation
|
||||
const newsletterSchema = z.object({
|
||||
email: z.email({ error: 'Please enter a valid email address' }),
|
||||
});
|
||||
|
||||
// Create a safe action for newsletter unsubscription
|
||||
export const unsubscribeNewsletterAction = actionClient
|
||||
export const unsubscribeNewsletterAction = userActionClient
|
||||
.schema(newsletterSchema)
|
||||
.action(async ({ parsedInput: { email } }) => {
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const unsubscribed = await unsubscribe(email);
|
||||
|
||||
|
@ -1,12 +1,9 @@
|
||||
'use server';
|
||||
|
||||
import { validateTurnstileToken } from '@/lib/captcha';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import { actionClient } from '@/lib/safe-action';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Create a safe action client
|
||||
const actionClient = createSafeActionClient();
|
||||
|
||||
// Captcha validation schema
|
||||
const captchaSchema = z.object({
|
||||
captchaToken: z.string().min(1, { error: 'Captcha token is required' }),
|
||||
|
57
src/lib/safe-action.ts
Normal file
57
src/lib/safe-action.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
import type { User } from './auth-types';
|
||||
import { isDemoWebsite } from './demo';
|
||||
import { getSession } from './server';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 1. Base action client – put global error handling / metadata here if needed
|
||||
// -----------------------------------------------------------------------------
|
||||
export const actionClient = createSafeActionClient({
|
||||
handleServerError: (e) => {
|
||||
if (e instanceof Error) {
|
||||
return {
|
||||
success: false,
|
||||
error: e.message,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: 'Something went wrong while executing the action',
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 2. Auth-guarded client
|
||||
// -----------------------------------------------------------------------------
|
||||
export const userActionClient = actionClient.use(async ({ next }) => {
|
||||
const session = await getSession();
|
||||
|
||||
if (!session?.user) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
};
|
||||
}
|
||||
|
||||
return next({ ctx: { user: session.user } });
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 3. Admin-only client (extends auth client)
|
||||
// -----------------------------------------------------------------------------
|
||||
export const adminActionClient = userActionClient.use(async ({ next, ctx }) => {
|
||||
const user = (ctx as { user: User }).user;
|
||||
const isDemo = isDemoWebsite();
|
||||
const isAdmin = user.role === 'admin';
|
||||
// If this is a demo website and user is not an admin, allow the request
|
||||
if (!isAdmin && !isDemo) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
};
|
||||
}
|
||||
|
||||
return next({ ctx });
|
||||
});
|
Loading…
Reference in New Issue
Block a user