Refactor Docker Compose configuration to use dynamic volume paths, update deployment workflow to create necessary directories, and enhance Prisma schema with public visibility for evaluations. Improve access control in API routes and adjust evaluation creation logic to include public visibility. Fix minor issues in login and evaluation pages for better user experience.
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 2m17s

This commit is contained in:
Julien Froidefond
2026-02-20 13:13:41 +01:00
parent f5cbc578b7
commit 9d8d1b257d
12 changed files with 52 additions and 14 deletions

View File

@@ -3,15 +3,21 @@ import { Prisma } from "@prisma/client";
import { auth } from "@/auth";
import { prisma } from "@/lib/db";
async function canAccessEvaluation(evaluationId: string, userId: string, isAdmin: boolean) {
async function canAccessEvaluation(
evaluationId: string,
userId: string,
isAdmin: boolean,
readOnly = false
) {
if (isAdmin) return true;
const eval_ = await prisma.evaluation.findUnique({
where: { id: evaluationId },
select: { evaluatorId: true, sharedWith: { select: { userId: true } } },
select: { evaluatorId: true, isPublic: true, sharedWith: { select: { userId: true } } },
});
if (!eval_) return false;
if (eval_.evaluatorId === userId) return true;
if (eval_.sharedWith.some((s) => s.userId === userId)) return true;
if (readOnly && eval_.isPublic) return true;
return false;
}
@@ -45,7 +51,8 @@ export async function GET(
const hasAccess = await canAccessEvaluation(
id,
session.user.id,
session.user.role === "admin"
session.user.role === "admin",
true // read-only: public evals accessibles en lecture
);
if (!hasAccess) {
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });

View File

@@ -23,6 +23,7 @@ export async function GET(req: NextRequest) {
OR: [
{ evaluatorId: userId },
{ sharedWith: { some: { userId } } },
{ isPublic: true },
],
}),
},

View File

@@ -77,7 +77,7 @@ export default function LoginPage() {
<p className="mt-4 font-mono text-xs text-zinc-500 dark:text-zinc-400">
Pas de compte ?{" "}
<Link href="/auth/signup" className="text-cyan-600 dark:text-cyan-400 hover:underline">
S'inscrire
S&apos;inscrire
</Link>
</p>
</div>

View File

@@ -25,7 +25,7 @@ export default function NewEvaluationPage() {
const display = session.user.name || session.user.email || "";
setForm((f) => ({ ...f, evaluatorName: display }));
}
}, [session?.user?.name, session?.user?.email]);
}, [session?.user]);
useEffect(() => {
fetch("/api/templates")

View File

@@ -86,13 +86,13 @@ export function DimensionCard({ dimension, score, index, evaluationId, onScoreCh
useEffect(() => {
if (evaluationId && typeof window !== "undefined") {
const stored = getStoredExpanded(evaluationId, dimension.id);
if (stored !== null) setExpanded(stored);
if (stored !== null) queueMicrotask(() => setExpanded(stored));
}
}, [evaluationId, dimension.id]);
useEffect(() => {
if (collapseAllTrigger != null && collapseAllTrigger > 0) {
setExpanded(false);
queueMicrotask(() => setExpanded(false));
if (evaluationId) setStoredExpanded(evaluationId, dimension.id, false);
}
}, [collapseAllTrigger, evaluationId, dimension.id]);

View File

@@ -32,11 +32,11 @@ export function ShareModal({
sharedWith,
onUpdate,
}: ShareModalProps) {
if (!isOpen) return null;
const [shareUserId, setShareUserId] = useState("");
const [loading, setLoading] = useState(false);
if (!isOpen) return null;
const availableUsers = users.filter(
(u) => u.id !== evaluatorId && !sharedWith.some((s) => s.user.id === u.id)
);