init: pulseform v2 — Express/Prisma/React/SCSS/R3F stack

This commit is contained in:
2026-03-26 02:55:45 +00:00
commit 2d5030c521
31 changed files with 8131 additions and 0 deletions

51
backend/src/index.ts Normal file
View File

@@ -0,0 +1,51 @@
import express from "express";
import cors from "cors";
import cookieParser from "cookie-parser";
import { PrismaClient } from "@prisma/client";
const app = express();
const prisma = new PrismaClient();
const PORT = process.env.PORT ?? 4000;
const CLIENT_URL = process.env.CLIENT_URL ?? "http://localhost:5173";
if (!process.env.JWT_SECRET) {
throw new Error("JWT_SECRET manquant — arrêt du serveur.");
}
// ─── Middlewares ──────────────────────────────────────────────────────────────
app.use(
cors({
origin: CLIENT_URL,
credentials: true,
})
);
app.use(express.json());
app.use(cookieParser());
// ─── Static uploads ──────────────────────────────────────────────────────────
app.use("/uploads", express.static("uploads"));
// ─── Routes ──────────────────────────────────────────────────────────────────
// app.use("/api/auth", authRoutes);
// app.use("/api/users", userRoutes);
// app.use("/api/exercises", exerciseRoutes);
// app.use("/api/programs", programRoutes);
// app.use("/api/groups", groupRoutes);
// app.use("/api/friends", friendRoutes);
// app.use("/api/history", historyRoutes);
app.get("/health", (_req, res) => {
res.json({ status: "ok" });
});
// ─── Start ────────────────────────────────────────────────────────────────────
app.listen(PORT, () => {
console.log(`Serveur démarré sur http://localhost:${PORT}`);
});
export { prisma };

View File

@@ -0,0 +1,31 @@
import type { Response, NextFunction } from "express";
import jwt from "jsonwebtoken";
import type { AppRequest, AuthPayload } from "../types/context.js";
export function requireAuth(req: AppRequest, res: Response, next: NextFunction): void {
const token = req.cookies?.token as string | undefined;
if (!token) {
res.status(401).json({ message: "Non authentifié." });
return;
}
try {
const secret = process.env.JWT_SECRET as string;
const payload = jwt.verify(token, secret) as AuthPayload;
req.user = payload;
next();
} catch {
res.status(401).json({ message: "Token invalide ou expiré." });
}
}
export function requireRole(...roles: AuthPayload["role"][]) {
return (req: AppRequest, res: Response, next: NextFunction): void => {
if (!req.user || !roles.includes(req.user.role)) {
res.status(403).json({ message: "Accès refusé." });
return;
}
next();
};
}

View File

@@ -0,0 +1,17 @@
import type { Request, Response } from "express";
import type { Role } from "@prisma/client";
export interface AuthPayload {
id: string;
email: string;
role: Role;
}
export interface AppRequest extends Request {
user?: AuthPayload;
}
export interface AppContext {
req: AppRequest;
res: Response;
}