W
Wasp2w ago
2euxs

Comment autoriser les admins a accéder a une page

Comment autoriser les admins a accéder a une page dans la template opensaas ?
10 Replies
kapa.ai
kapa.ai2w ago
Hi @2euxs. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Pour autoriser les administrateurs à accéder à une page spécifique dans le template OpenSaaS de Wasp, voici les étapes principales à suivre : 1. Assurez-vous d'avoir un champ isAdmin dans votre entité User. Cela semble déjà être le cas dans le template OpenSaaS, comme indiqué dans l'un des documents :
isAdmin Boolean @default(true)
isAdmin Boolean @default(true)
2. Pour la page que vous souhaitez protéger, utilisez la propriété authRequired: true dans la déclaration de la page. Par exemple, pour la page du tableau de bord administrateur :
route AdminRoute { path: "/admin", to: DashboardPage }
page DashboardPage {
authRequired: true,
component: import Dashboard from "@client/admin/pages/DashboardPage"
}
route AdminRoute { path: "/admin", to: DashboardPage }
page DashboardPage {
authRequired: true,
component: import Dashboard from "@client/admin/pages/DashboardPage"
}
3. Dans le composant React de la page, vous devrez vérifier si l'utilisateur est un administrateur. Vous pouvez utiliser le prop user fourni automatiquement lorsque authRequired est true. 4. Implémentez la logique de vérification des droits d'administrateur dans vos actions et requêtes côté serveur. Vous pouvez vérifier la valeur du champ isAdmin de l'utilisateur et renvoyer une erreur 403 si l'utilisateur n'est pas administrateur. Il est important de noter que Wasp ne fournit pas actuellement de système de contrôle d'accès basé sur les rôles (RBAC) intégré. Comme mentionné dans ce fil de discussion Discord (https://discord.com/channels/686873244791210014/1211733266235916358), vous devrez implémenter cette logique vous-même pour le moment. Pour une implémentation plus détaillée, vous pouvez vous inspirer du template OpenSaaS qui inclut déjà le concept d'administrateurs et un tableau de bord administrateur. Vous pouvez consulter ce template à l'adresse suivante : <https://opensaas.sh/> (https://opensaas.sh/) N'oubliez pas d'implémenter ces vérifications à la fois côté client et côté serveur pour une sécurité optimale. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @2euxs, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
2euxs
2euxsOP2w ago
@kapa.ai Code moi un algorithme qui autorise les admin à accéder a une page pendant que ceux qui n'ont pas d'abonnement ne peuvent pas le faire
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Je comprends que vous souhaitez un algorithme pour autoriser l'accès à une page uniquement aux administrateurs et aux utilisateurs ayant un abonnement. Voici un exemple de code basé sur les informations fournies dans les sources :
import { HttpError } from 'wasp/server'

export const checkAdminOrSubscriptionAccess = async (args, context) => {
if (!context.user) {
throw new HttpError(401) // Unauthorized if not logged in
}

const user = await context.entities.User.findUnique({
where: { id: context.user.id },
select: { isAdmin: true, subscriptionStatus: true }
})

if (!user) {
throw new HttpError(404) // User not found
}

if (user.isAdmin) {
return true // Admins always have access
}

if (user.subscriptionStatus === 'active') {
return true // Users with active subscription have access
}

throw new HttpError(403) // Forbidden for non-admins without active subscription
}
import { HttpError } from 'wasp/server'

export const checkAdminOrSubscriptionAccess = async (args, context) => {
if (!context.user) {
throw new HttpError(401) // Unauthorized if not logged in
}

const user = await context.entities.User.findUnique({
where: { id: context.user.id },
select: { isAdmin: true, subscriptionStatus: true }
})

if (!user) {
throw new HttpError(404) // User not found
}

if (user.isAdmin) {
return true // Admins always have access
}

if (user.subscriptionStatus === 'active') {
return true // Users with active subscription have access
}

throw new HttpError(403) // Forbidden for non-admins without active subscription
}
Vous pouvez utiliser cet algorithme dans vos actions ou requêtes Wasp. Par exemple :
import { checkAdminOrSubscriptionAccess } from './auth'

export const getProtectedData = async (args, context) => {
await checkAdminOrSubscriptionAccess(args, context)

// Si l'accès est autorisé, continuez avec la logique de votre action/requête
// ...
}
import { checkAdminOrSubscriptionAccess } from './auth'

export const getProtectedData = async (args, context) => {
await checkAdminOrSubscriptionAccess(args, context)

// Si l'accès est autorisé, continuez avec la logique de votre action/requête
// ...
}
Pour l'utiliser dans une page, vous devrez d'abord définir la page comme nécessitant une authentification dans votre fichier main.wasp :
page ProtectedPage {
authRequired: true,
component: import { ProtectedPage } from "@src/ProtectedPage"
}
page ProtectedPage {
authRequired: true,
component: import { ProtectedPage } from "@src/ProtectedPage"
}
Ensuite, dans votre composant React, vous pouvez vérifier le statut de l'utilisateur :
import { useQuery } from 'wasp/client/react'
import { getProtectedData } from 'wasp/client/operations'
export function ProtectedPage({ user }) {
const { data, isLoading, error } = useQuery(getProtectedData)

if (isLoading) return 'Loading...'
if (error) return 'Access denied'

return (
<div>
<h1>Protected Page</h1>
{/* Affichez ici le contenu protégé */}
</div>
)
}
import { useQuery } from 'wasp/client/react'
import { getProtectedData } from 'wasp/client/operations'
export function ProtectedPage({ user }) {
const { data, isLoading, error } = useQuery(getProtectedData)

if (isLoading) return 'Loading...'
if (error) return 'Access denied'

return (
<div>
<h1>Protected Page</h1>
{/* Affichez ici le contenu protégé */}
</div>
)
}
Ce code vérifie si l'utilisateur est un administrateur ou s'il a un abonnement actif. Il utilise le champ isAdmin et subscriptionStatus de l'entité User, comme mentionné dans les sources. Notez que cette implémentation suppose que vous avez déjà configuré correctement votre modèle User avec les champs isAdmin et subscriptionStatus. Assurez-vous également d'avoir mis en place la logique pour gérer les abonnements et mettre à jour le statut d'abonnement des utilisateurs. Sources : - Discord support thread (https://discord.com/channels/686873244791210014/1214979650803998781) - Docs > Tutorial > 7. Adding Authentication > Updating Operations to Check Authentication (https://wasp-lang.dev/docs/tutorial/auth#updating-operations-to-check-authentication) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @2euxs, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
2euxs
2euxsOP2w ago
@kapa.ai Je veux faire en sorte de pouvoir rajouter des credits aux utilisateurs dans la dashbord admin code moi un algorithme qui me permet de faire ça @kapa.ai pa.ai Je veux faire en sorte de pouvoir rajouter des credits aux utilisateurs dans la dashbord admin code moi un algorithme qui me permet de faire ça
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- D'accord, je comprends que vous souhaitez ajouter une fonctionnalité pour permettre aux administrateurs d'ajouter des crédits aux utilisateurs depuis le tableau de bord administrateur. Voici un exemple d'algorithme basé sur les informations disponibles dans les sources fournies :
import HttpError from '@wasp/core/HttpError.js'

export const addCreditsToUser = async (args, context) => {
// Vérifier si l'utilisateur actuel est un administrateur
if (!context.user || !context.user.isAdmin) {
throw new HttpError(403, "Accès non autorisé. Seuls les administrateurs peuvent ajouter des crédits.")
}

const { userId, creditsToAdd } = args

if (!userId || !creditsToAdd || creditsToAdd <= 0) {
throw new HttpError(400, "UserId et un nombre positif de crédits sont requis.")
}

try {
const updatedUser = await context.entities.User.update({
where: { id: userId },
data: {
credits: {
increment: creditsToAdd
}
}
})

return {
success: true,
message: `${creditsToAdd} crédits ajoutés à l'utilisateur ${updatedUser.email}`,
newCreditBalance: updatedUser.credits
}
} catch (error) {
throw new HttpError(500, "Erreur lors de l'ajout des crédits : " + error.message)
}
}
import HttpError from '@wasp/core/HttpError.js'

export const addCreditsToUser = async (args, context) => {
// Vérifier si l'utilisateur actuel est un administrateur
if (!context.user || !context.user.isAdmin) {
throw new HttpError(403, "Accès non autorisé. Seuls les administrateurs peuvent ajouter des crédits.")
}

const { userId, creditsToAdd } = args

if (!userId || !creditsToAdd || creditsToAdd <= 0) {
throw new HttpError(400, "UserId et un nombre positif de crédits sont requis.")
}

try {
const updatedUser = await context.entities.User.update({
where: { id: userId },
data: {
credits: {
increment: creditsToAdd
}
}
})

return {
success: true,
message: `${creditsToAdd} crédits ajoutés à l'utilisateur ${updatedUser.email}`,
newCreditBalance: updatedUser.credits
}
} catch (error) {
throw new HttpError(500, "Erreur lors de l'ajout des crédits : " + error.message)
}
}
Pour utiliser cet algorithme, vous devrez l'intégrer dans votre application Wasp. Voici comment procéder : 1. Ajoutez cette action dans votre fichier main.wasp :
action addCreditsToUser {
fn: import { addCreditsToUser } from "@server/actions.js",
entities: [User]
}
action addCreditsToUser {
fn: import { addCreditsToUser } from "@server/actions.js",
entities: [User]
}
2. Dans votre composant React du tableau de bord administrateur, vous pouvez utiliser cette action comme suit :
import { useAction } from 'wasp/client/react'
import { addCreditsToUser } from 'wasp/client/operations'
function AdminDashboard() {
const addCreditsToUserFn = useAction(addCreditsToUser)

const handleAddCredits = async (userId, creditsToAdd) => {
try {
const result = await addCreditsToUserFn({ userId, creditsToAdd })
alert(result.message)
} catch (error) {
alert("Erreur : " + error.message)
}
}

return (
<div>
{/* Votre interface utilisateur pour sélectionner un utilisateur et entrer le nombre de crédits */}
<button onClick={() => handleAddCredits('user-id', 10)}>Ajouter 10 crédits</button>
</div>
)
}
import { useAction } from 'wasp/client/react'
import { addCreditsToUser } from 'wasp/client/operations'
function AdminDashboard() {
const addCreditsToUserFn = useAction(addCreditsToUser)

const handleAddCredits = async (userId, creditsToAdd) => {
try {
const result = await addCreditsToUserFn({ userId, creditsToAdd })
alert(result.message)
} catch (error) {
alert("Erreur : " + error.message)
}
}

return (
<div>
{/* Votre interface utilisateur pour sélectionner un utilisateur et entrer le nombre de crédits */}
<button onClick={() => handleAddCredits('user-id', 10)}>Ajouter 10 crédits</button>
</div>
)
}
Cet algorithme vérifie d'abord si l'utilisateur actuel est un administrateur. Ensuite, il met à jour le nombre de crédits de l'utilisateur spécifié en utilisant l'opération increment de Prisma. N'oubliez pas d'adapter ce code à votre structure de données exacte et à vos besoins spécifiques. Assurez-vous également que votre modèle User dans Prisma inclut un champ credits. Sources : - Discord support thread: Payment for Credits / Credit Balance System > Answers Part 1 (https://discord.com/channels/686873244791210014/1205328655300038766) - Discord support thread: Update DB Field using Another Field > Answers (https://discord.com/channels/686873244791210014/1216150994157834350) N'hésitez pas si vous avez besoin de plus de précisions ou d'adaptations pour votre cas spécifique ! ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @2euxs, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
2euxs
2euxsOP7d ago
@kapa.ai On est d'accord que pour augmenter le nombre de crédits il faut le faire de la façon suivante : await context.entities.User.update({ where: { id: userId, }, data: { credits: { increment: credits, }, }, });
kapa.ai
kapa.ai7d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Oui, vous avez tout à fait raison. La méthode que vous avez présentée est la façon correcte d'augmenter le nombre de crédits d'un utilisateur en utilisant Prisma dans le contexte de Wasp. Votre code est correct et correspond aux meilleures pratiques pour effectuer cette opération. Cette approche utilise l'opération increment de Prisma, qui est la méthode recommandée pour augmenter la valeur d'un champ numérique de manière atomique. Cela garantit que l'opération est sûre même en cas d'accès concurrents à la base de données. Voici une explication détaillée de votre code :
await context.entities.User.update({
where: {
id: userId,
},
data: {
credits: {
increment: credits,
},
},
});
await context.entities.User.update({
where: {
id: userId,
},
data: {
credits: {
increment: credits,
},
},
});
- context.entities.User accède à l'entité User via le contexte fourni par Wasp. - La méthode update est utilisée pour modifier un enregistrement existant. - where: { id: userId } spécifie quel utilisateur doit être mis à jour. - data: { credits: { increment: credits } } indique que le champ credits doit être incrémenté de la valeur spécifiée. Cette approche est cohérente avec les exemples fournis dans les sources, notamment dans Discord support thread: Payment for Credits / Credit Balance System > Answers Part 4 (https://discord.com/channels/686873244791210014/1205328655300038766), où une opération similaire est utilisée pour incrémenter les crédits d'un utilisateur. C'est une méthode sûre et efficace pour mettre à jour les crédits d'un utilisateur dans une application Wasp utilisant Prisma comme ORM. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @2euxs, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
2euxs
2euxsOP7d ago
Ajoute ici une option pour ajouter un nombre de crédits à un utilisateur :
2euxs
2euxsOP7d ago
@kapa.ai Ajoute ici une option pour ajouter un nombre de crédits à un utilisateur :
kapa.ai
kapa.ai7d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- D'accord, je vais vous montrer comment ajouter une option pour ajouter un nombre spécifique de crédits à un utilisateur. Voici un exemple de code qui étend l'algorithme précédent pour inclure cette fonctionnalité :
import HttpError from '@wasp/core/HttpError.js'

export const addCreditsToUser = async (args, context) => {
// Vérifier si l'utilisateur actuel est un administrateur
if (!context.user || !context.user.isAdmin) {
throw new HttpError(403, "Accès non autorisé. Seuls les administrateurs peuvent ajouter des crédits.")
}

const { userId, creditsToAdd } = args

if (!userId || !creditsToAdd || isNaN(creditsToAdd) || creditsToAdd <= 0) {
throw new HttpError(400, "UserId et un nombre positif de crédits sont requis.")
}

try {
const updatedUser = await context.entities.User.update({
where: {
id: userId,
},
data: {
credits: {
increment: creditsToAdd,
},
},
});

return {
success: true,
message: `${creditsToAdd} crédits ajoutés à l'utilisateur ${updatedUser.email}`,
newCreditBalance: updatedUser.credits
}
} catch (error) {
throw new HttpError(500, "Erreur lors de l'ajout des crédits : " + error.message)
}
}
import HttpError from '@wasp/core/HttpError.js'

export const addCreditsToUser = async (args, context) => {
// Vérifier si l'utilisateur actuel est un administrateur
if (!context.user || !context.user.isAdmin) {
throw new HttpError(403, "Accès non autorisé. Seuls les administrateurs peuvent ajouter des crédits.")
}

const { userId, creditsToAdd } = args

if (!userId || !creditsToAdd || isNaN(creditsToAdd) || creditsToAdd <= 0) {
throw new HttpError(400, "UserId et un nombre positif de crédits sont requis.")
}

try {
const updatedUser = await context.entities.User.update({
where: {
id: userId,
},
data: {
credits: {
increment: creditsToAdd,
},
},
});

return {
success: true,
message: `${creditsToAdd} crédits ajoutés à l'utilisateur ${updatedUser.email}`,
newCreditBalance: updatedUser.credits
}
} catch (error) {
throw new HttpError(500, "Erreur lors de l'ajout des crédits : " + error.message)
}
}
Pour utiliser cette fonction dans votre interface utilisateur d'administration, vous pouvez créer un composant React comme celui-ci :
import React, { useState } from 'react'
import { useAction } from 'wasp/client/react'
import { addCreditsToUser } from 'wasp/client/operations'

function AddCreditsForm() {
const [userId, setUserId] = useState('')
const [creditsToAdd, setCreditsToAdd] = useState('')
const addCreditsToUserFn = useAction(addCreditsToUser)
const handleSubmit = async (e) => {
e.preventDefault()
try {
const result = await addCreditsToUserFn({
userId,
creditsToAdd: parseInt(creditsToAdd, 10)
})
alert(result.message)
// Réinitialiser le formulaire
setUserId('')
setCreditsToAdd('')
} catch (error) {
alert("Erreur : " + error.message)
}
}

return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={userId}
onChange={(e) => setUserId(e.target.value)}
placeholder="ID de l'utilisateur"
required
/>
<input
type="number"
value={creditsToAdd}
onChange={(e) => setCreditsToAdd(e.target.value)}
placeholder="Nombre de crédits à ajouter"
required
min="1"
/>
<button type="submit">Ajouter des crédits</button>
</form>
)
}
import React, { useState } from 'react'
import { useAction } from 'wasp/client/react'
import { addCreditsToUser } from 'wasp/client/operations'

function AddCreditsForm() {
const [userId, setUserId] = useState('')
const [creditsToAdd, setCreditsToAdd] = useState('')
const addCreditsToUserFn = useAction(addCreditsToUser)
const handleSubmit = async (e) => {
e.preventDefault()
try {
const result = await addCreditsToUserFn({
userId,
creditsToAdd: parseInt(creditsToAdd, 10)
})
alert(result.message)
// Réinitialiser le formulaire
setUserId('')
setCreditsToAdd('')
} catch (error) {
alert("Erreur : " + error.message)
}
}

return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={userId}
onChange={(e) => setUserId(e.target.value)}
placeholder="ID de l'utilisateur"
required
/>
<input
type="number"
value={creditsToAdd}
onChange={(e) => setCreditsToAdd(e.target.value)}
placeholder="Nombre de crédits à ajouter"
required
min="1"
/>
<button type="submit">Ajouter des crédits</button>
</form>
)
}
Ce code crée un formulaire qui permet à un administrateur d'entrer l'ID de l'utilisateur et le nombre de crédits à ajouter. Lorsque le formulaire est soumis, il appelle la fonction addCreditsToUser que nous avons définie précédemment. N'oubliez pas d'ajouter ce composant à votre page de tableau de bord administrateur pour que les administrateurs puissent l'utiliser. Cette implémentation utilise la méthode correcte pour incrémenter les crédits, comme vous l'avez mentionné :
await context.entities.User.update({
where: {
id: userId,
},
data: {
credits: {
increment: creditsToAdd,
},
},
});
await context.entities.User.update({
where: {
id: userId,
},
data: {
credits: {
increment: creditsToAdd,
},
},
});
Cette approche garantit que l'opération d'ajout de crédits est effectuée de manière sûre et atomique, en utilisant l'opération increment de Prisma. Discord support thread: Payment for Credits / Credit Balance System > Answers Part 4 (https://discord.com/channels/686873244791210014/1205328655300038766) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @2euxs, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?

Did you find this page helpful?