Refactor admin action naming and move user assertions
Renamed the admin actions to have a more uniform approach with "Action" suffixed. This makes the action function's purpose clear in the codebase. Also, moved the `assertUserIsNotCurrentSuperAdmin` method to a more suitable place in the code, arranging it in a private scope. This helps in maintaining clean code architecture and enhances readibility.
This commit is contained in:
@@ -42,10 +42,13 @@ function getClassName() {
|
|||||||
const theme = themeCookie ?? appConfig.theme;
|
const theme = themeCookie ?? appConfig.theme;
|
||||||
const dark = theme === 'dark';
|
const dark = theme === 'dark';
|
||||||
|
|
||||||
return cn('min-h-screen bg-background antialiased', {
|
return cn(
|
||||||
dark,
|
'min-h-screen bg-background antialiased',
|
||||||
[sans.className]: true,
|
{
|
||||||
});
|
dark,
|
||||||
|
},
|
||||||
|
sans.className,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { getOrder, getVariant } from '@lemonsqueezy/lemonsqueezy.js';
|
import { getOrder, getVariant } from '@lemonsqueezy/lemonsqueezy.js';
|
||||||
import { createHmac, timingSafeEqual } from 'crypto';
|
import { createHmac, timingSafeEqual } from 'node:crypto';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BillingConfig,
|
BillingConfig,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
} from '@kit/ui/form';
|
} from '@kit/ui/form';
|
||||||
import { Input } from '@kit/ui/input';
|
import { Input } from '@kit/ui/input';
|
||||||
|
|
||||||
import { banUser } from '../lib/server/admin-server-actions';
|
import { banUserAction } from '../lib/server/admin-server-actions';
|
||||||
import { BanUserSchema } from '../lib/server/schema/admin-actions.schema';
|
import { BanUserSchema } from '../lib/server/schema/admin-actions.schema';
|
||||||
|
|
||||||
export function AdminBanUserDialog(
|
export function AdminBanUserDialog(
|
||||||
@@ -58,7 +58,7 @@ export function AdminBanUserDialog(
|
|||||||
<form
|
<form
|
||||||
className={'flex flex-col space-y-8'}
|
className={'flex flex-col space-y-8'}
|
||||||
onSubmit={form.handleSubmit((data) => {
|
onSubmit={form.handleSubmit((data) => {
|
||||||
return banUser(data);
|
return banUserAction(data);
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import {
|
|||||||
} from '@kit/ui/form';
|
} from '@kit/ui/form';
|
||||||
import { Input } from '@kit/ui/input';
|
import { Input } from '@kit/ui/input';
|
||||||
|
|
||||||
import { deleteAccount } from '../lib/server/admin-server-actions';
|
import { deleteAccountAction } from '../lib/server/admin-server-actions';
|
||||||
import { DeleteAccountSchema } from '../lib/server/schema/admin-actions.schema';
|
import { DeleteAccountSchema } from '../lib/server/schema/admin-actions.schema';
|
||||||
|
|
||||||
export function AdminDeleteAccountDialog(
|
export function AdminDeleteAccountDialog(
|
||||||
@@ -59,7 +59,7 @@ export function AdminDeleteAccountDialog(
|
|||||||
<form
|
<form
|
||||||
className={'flex flex-col space-y-8'}
|
className={'flex flex-col space-y-8'}
|
||||||
onSubmit={form.handleSubmit((data) => {
|
onSubmit={form.handleSubmit((data) => {
|
||||||
return deleteAccount(data);
|
return deleteAccountAction(data);
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
} from '@kit/ui/form';
|
} from '@kit/ui/form';
|
||||||
import { Input } from '@kit/ui/input';
|
import { Input } from '@kit/ui/input';
|
||||||
|
|
||||||
import { deleteUser } from '../lib/server/admin-server-actions';
|
import { deleteUserAction } from '../lib/server/admin-server-actions';
|
||||||
import { DeleteUserSchema } from '../lib/server/schema/admin-actions.schema';
|
import { DeleteUserSchema } from '../lib/server/schema/admin-actions.schema';
|
||||||
|
|
||||||
export function AdminDeleteUserDialog(
|
export function AdminDeleteUserDialog(
|
||||||
@@ -60,7 +60,7 @@ export function AdminDeleteUserDialog(
|
|||||||
<form
|
<form
|
||||||
className={'flex flex-col space-y-8'}
|
className={'flex flex-col space-y-8'}
|
||||||
onSubmit={form.handleSubmit((data) => {
|
onSubmit={form.handleSubmit((data) => {
|
||||||
return deleteUser(data);
|
return deleteUserAction(data);
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import {
|
|||||||
import { Input } from '@kit/ui/input';
|
import { Input } from '@kit/ui/input';
|
||||||
import { LoadingOverlay } from '@kit/ui/loading-overlay';
|
import { LoadingOverlay } from '@kit/ui/loading-overlay';
|
||||||
|
|
||||||
import { impersonateUser } from '../lib/server/admin-server-actions';
|
import { impersonateUserAction } from '../lib/server/admin-server-actions';
|
||||||
import { ImpersonateUserSchema } from '../lib/server/schema/admin-actions.schema';
|
import { ImpersonateUserSchema } from '../lib/server/schema/admin-actions.schema';
|
||||||
|
|
||||||
export function AdminImpersonateUserDialog(
|
export function AdminImpersonateUserDialog(
|
||||||
@@ -81,7 +81,7 @@ export function AdminImpersonateUserDialog(
|
|||||||
<form
|
<form
|
||||||
className={'flex flex-col space-y-8'}
|
className={'flex flex-col space-y-8'}
|
||||||
onSubmit={form.handleSubmit(async (data) => {
|
onSubmit={form.handleSubmit(async (data) => {
|
||||||
const tokens = await impersonateUser(data);
|
const tokens = await impersonateUserAction(data);
|
||||||
|
|
||||||
setTokens(tokens);
|
setTokens(tokens);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
} from '@kit/ui/form';
|
} from '@kit/ui/form';
|
||||||
import { Input } from '@kit/ui/input';
|
import { Input } from '@kit/ui/input';
|
||||||
|
|
||||||
import { reactivateUser } from '../lib/server/admin-server-actions';
|
import { reactivateUserAction } from '../lib/server/admin-server-actions';
|
||||||
import { ReactivateUserSchema } from '../lib/server/schema/admin-actions.schema';
|
import { ReactivateUserSchema } from '../lib/server/schema/admin-actions.schema';
|
||||||
|
|
||||||
export function AdminReactivateUserDialog(
|
export function AdminReactivateUserDialog(
|
||||||
@@ -58,7 +58,7 @@ export function AdminReactivateUserDialog(
|
|||||||
<form
|
<form
|
||||||
className={'flex flex-col space-y-8'}
|
className={'flex flex-col space-y-8'}
|
||||||
onSubmit={form.handleSubmit((data) => {
|
onSubmit={form.handleSubmit((data) => {
|
||||||
return reactivateUser(data);
|
return reactivateUserAction(data);
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ import { AdminAccountsService } from './services/admin-accounts.service';
|
|||||||
import { AdminAuthUserService } from './services/admin-auth-user.service';
|
import { AdminAuthUserService } from './services/admin-auth-user.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name banUser
|
* @name banUserAction
|
||||||
* @description Ban a user from the system.
|
* @description Ban a user from the system.
|
||||||
*/
|
*/
|
||||||
export const banUser = enhanceAdminAction(
|
export const banUserAction = enhanceAdminAction(
|
||||||
enhanceAction(
|
enhanceAction(
|
||||||
async ({ userId }) => {
|
async ({ userId }) => {
|
||||||
const service = getAdminAuthService();
|
const service = getAdminAuthService();
|
||||||
@@ -41,10 +41,10 @@ export const banUser = enhanceAdminAction(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name reactivateUser
|
* @name reactivateUserAction
|
||||||
* @description Reactivate a user in the system.
|
* @description Reactivate a user in the system.
|
||||||
*/
|
*/
|
||||||
export const reactivateUser = enhanceAdminAction(
|
export const reactivateUserAction = enhanceAdminAction(
|
||||||
enhanceAction(
|
enhanceAction(
|
||||||
async ({ userId }) => {
|
async ({ userId }) => {
|
||||||
const service = getAdminAuthService();
|
const service = getAdminAuthService();
|
||||||
@@ -64,10 +64,10 @@ export const reactivateUser = enhanceAdminAction(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name impersonateUser
|
* @name impersonateUserAction
|
||||||
* @description Impersonate a user in the system.
|
* @description Impersonate a user in the system.
|
||||||
*/
|
*/
|
||||||
export const impersonateUser = enhanceAdminAction(
|
export const impersonateUserAction = enhanceAdminAction(
|
||||||
enhanceAction(
|
enhanceAction(
|
||||||
async ({ userId }) => {
|
async ({ userId }) => {
|
||||||
const service = getAdminAuthService();
|
const service = getAdminAuthService();
|
||||||
@@ -81,10 +81,10 @@ export const impersonateUser = enhanceAdminAction(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name deleteUser
|
* @name deleteUserAction
|
||||||
* @description Delete a user from the system.
|
* @description Delete a user from the system.
|
||||||
*/
|
*/
|
||||||
export const deleteUser = enhanceAdminAction(
|
export const deleteUserAction = enhanceAdminAction(
|
||||||
enhanceAction(
|
enhanceAction(
|
||||||
async ({ userId }) => {
|
async ({ userId }) => {
|
||||||
const service = getAdminAuthService();
|
const service = getAdminAuthService();
|
||||||
@@ -102,10 +102,10 @@ export const deleteUser = enhanceAdminAction(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name deleteAccount
|
* @name deleteAccountAction
|
||||||
* @description Delete an account from the system.
|
* @description Delete an account from the system.
|
||||||
*/
|
*/
|
||||||
export const deleteAccount = enhanceAdminAction(
|
export const deleteAccountAction = enhanceAdminAction(
|
||||||
enhanceAction(
|
enhanceAction(
|
||||||
async ({ accountId }) => {
|
async ({ accountId }) => {
|
||||||
const service = getAdminAccountsService();
|
const service = getAdminAccountsService();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'server-only';
|
|
||||||
|
|
||||||
import { SupabaseClient } from '@supabase/supabase-js';
|
import { SupabaseClient } from '@supabase/supabase-js';
|
||||||
|
|
||||||
|
import 'server-only';
|
||||||
|
|
||||||
import { Database } from '@kit/supabase/database';
|
import { Database } from '@kit/supabase/database';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,21 +15,10 @@ export class AdminAuthUserService {
|
|||||||
private readonly adminClient: SupabaseClient<Database>,
|
private readonly adminClient: SupabaseClient<Database>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async assertUserIsNotCurrentSuperAdmin(targetUserId: string) {
|
/**
|
||||||
const { data: user } = await this.client.auth.getUser();
|
* Delete a user by deleting the user record and auth record.
|
||||||
const currentUserId = user.user?.id;
|
* @param userId
|
||||||
|
*/
|
||||||
if (!currentUserId) {
|
|
||||||
throw new Error(`Error fetching user`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentUserId === targetUserId) {
|
|
||||||
throw new Error(
|
|
||||||
`You cannot perform a destructive action on your own account as a Super Admin`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteUser(userId: string) {
|
async deleteUser(userId: string) {
|
||||||
await this.assertUserIsNotCurrentSuperAdmin(userId);
|
await this.assertUserIsNotCurrentSuperAdmin(userId);
|
||||||
|
|
||||||
@@ -41,19 +30,33 @@ export class AdminAuthUserService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ban a user by setting the ban duration to `876600h` (100 years).
|
||||||
|
* @param userId
|
||||||
|
*/
|
||||||
async banUser(userId: string) {
|
async banUser(userId: string) {
|
||||||
await this.assertUserIsNotCurrentSuperAdmin(userId);
|
await this.assertUserIsNotCurrentSuperAdmin(userId);
|
||||||
|
|
||||||
return this.setBanDuration(userId, `876600h`);
|
return this.setBanDuration(userId, `876600h`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reactivate a user by setting the ban duration to `none`.
|
||||||
|
* @param userId
|
||||||
|
*/
|
||||||
async reactivateUser(userId: string) {
|
async reactivateUser(userId: string) {
|
||||||
await this.assertUserIsNotCurrentSuperAdmin(userId);
|
await this.assertUserIsNotCurrentSuperAdmin(userId);
|
||||||
|
|
||||||
return this.setBanDuration(userId, `none`);
|
return this.setBanDuration(userId, `none`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Impersonate a user by generating a magic link and returning the access and refresh tokens.
|
||||||
|
* @param userId
|
||||||
|
*/
|
||||||
async impersonateUser(userId: string) {
|
async impersonateUser(userId: string) {
|
||||||
|
await this.assertUserIsNotCurrentSuperAdmin(userId);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: { user },
|
data: { user },
|
||||||
error,
|
error,
|
||||||
@@ -110,6 +113,25 @@ export class AdminAuthUserService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that the target user is not the current user.
|
||||||
|
* @param targetUserId
|
||||||
|
*/
|
||||||
|
private async assertUserIsNotCurrentSuperAdmin(targetUserId: string) {
|
||||||
|
const { data: user } = await this.client.auth.getUser();
|
||||||
|
const currentUserId = user.user?.id;
|
||||||
|
|
||||||
|
if (!currentUserId) {
|
||||||
|
throw new Error(`Error fetching user`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUserId === targetUserId) {
|
||||||
|
throw new Error(
|
||||||
|
`You cannot perform a destructive action on your own account as a Super Admin`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async setBanDuration(userId: string, banDuration: string) {
|
private async setBanDuration(userId: string, banDuration: string) {
|
||||||
await this.adminClient.auth.admin.updateUserById(userId, {
|
await this.adminClient.auth.admin.updateUserById(userId, {
|
||||||
ban_duration: banDuration,
|
ban_duration: banDuration,
|
||||||
|
|||||||
Reference in New Issue
Block a user