fix(routes): resolve superOAuthId → DB userId — critical auth bug
req.user.id = SuperOAuth UUID, pas l'UUID TypeORM en DB. Sans ce fix : getUserPlanLevel retourne toujours 0, ownerId ne matche jamais. - video.routes: resolveDbUserId avant getUserPlanLevel - playlist.routes: resolveDbUserId sur toutes les opérations owner/member
This commit is contained in:
@@ -2,25 +2,34 @@ import { Router, Request, Response } from "express";
|
|||||||
import { AppDataSource } from "../config/data-source";
|
import { AppDataSource } from "../config/data-source";
|
||||||
import { Playlist } from "../entities/Playlist";
|
import { Playlist } from "../entities/Playlist";
|
||||||
import { PlaylistShare } from "../entities/PlaylistShare";
|
import { PlaylistShare } from "../entities/PlaylistShare";
|
||||||
|
import { User } from "../entities/User";
|
||||||
import { requireAuth, AuthenticatedRequest } from "../middleware/auth.middleware";
|
import { requireAuth, AuthenticatedRequest } from "../middleware/auth.middleware";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
|
/** Résout le superOAuthId vers l'UUID DB, crée le user si inexistant */
|
||||||
|
async function resolveDbUserId(superOAuthId: string): Promise<string | null> {
|
||||||
|
const user = await AppDataSource.getRepository(User).findOne({ where: { superOAuthId } });
|
||||||
|
return user?.id ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /api/playlists
|
* GET /api/playlists
|
||||||
* Playlists publiques + playlists partagées avec l'utilisateur connecté.
|
* Playlists publiques + playlists partagées avec l'utilisateur connecté.
|
||||||
*/
|
*/
|
||||||
router.get("/", requireAuth, async (req: Request, res: Response): Promise<void> => {
|
router.get("/", requireAuth, async (req: Request, res: Response): Promise<void> => {
|
||||||
const { user } = req as AuthenticatedRequest;
|
const { user } = req as AuthenticatedRequest;
|
||||||
|
const dbUserId = await resolveDbUserId(user.id);
|
||||||
|
if (!dbUserId) { res.status(401).json({ success: false, error: "USER_NOT_FOUND" }); return; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const owned = await AppDataSource.getRepository(Playlist).find({
|
const owned = await AppDataSource.getRepository(Playlist).find({
|
||||||
where: { ownerId: user.id },
|
where: { ownerId: dbUserId },
|
||||||
order: { createdAt: "DESC" },
|
order: { createdAt: "DESC" },
|
||||||
});
|
});
|
||||||
|
|
||||||
const shared = await AppDataSource.getRepository(PlaylistShare).find({
|
const shared = await AppDataSource.getRepository(PlaylistShare).find({
|
||||||
where: { userId: user.id, status: "active" },
|
where: { userId: dbUserId, status: "active" },
|
||||||
relations: ["playlist"],
|
relations: ["playlist"],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -53,10 +62,13 @@ router.post("/", requireAuth, async (req: Request, res: Response): Promise<void>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dbUserId = await resolveDbUserId(user.id);
|
||||||
|
if (!dbUserId) { res.status(401).json({ success: false, error: "USER_NOT_FOUND" }); return; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const playlist = AppDataSource.getRepository(Playlist).create({
|
const playlist = AppDataSource.getRepository(Playlist).create({
|
||||||
id: require("crypto").randomUUID(),
|
id: require("crypto").randomUUID(),
|
||||||
ownerId: user.id,
|
ownerId: dbUserId,
|
||||||
title: title.trim(),
|
title: title.trim(),
|
||||||
description: description ?? null,
|
description: description ?? null,
|
||||||
visibility: visibility ?? "private",
|
visibility: visibility ?? "private",
|
||||||
@@ -76,6 +88,8 @@ router.post("/", requireAuth, async (req: Request, res: Response): Promise<void>
|
|||||||
*/
|
*/
|
||||||
router.get("/:id", requireAuth, async (req: Request, res: Response): Promise<void> => {
|
router.get("/:id", requireAuth, async (req: Request, res: Response): Promise<void> => {
|
||||||
const { user } = req as AuthenticatedRequest;
|
const { user } = req as AuthenticatedRequest;
|
||||||
|
const dbUserId = await resolveDbUserId(user.id);
|
||||||
|
if (!dbUserId) { res.status(401).json({ success: false, error: "USER_NOT_FOUND" }); return; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const playlist = await AppDataSource.getRepository(Playlist).findOne({
|
const playlist = await AppDataSource.getRepository(Playlist).findOne({
|
||||||
@@ -88,8 +102,8 @@ router.get("/:id", requireAuth, async (req: Request, res: Response): Promise<voi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isOwner = playlist.ownerId === user.id;
|
const isOwner = playlist.ownerId === dbUserId;
|
||||||
const share = playlist.shares.find((s) => s.userId === user.id && s.status === "active");
|
const share = playlist.shares.find((s) => s.userId === dbUserId && s.status === "active");
|
||||||
const isPublic = playlist.visibility === "public";
|
const isPublic = playlist.visibility === "public";
|
||||||
|
|
||||||
if (!isOwner && !share && !isPublic) {
|
if (!isOwner && !share && !isPublic) {
|
||||||
@@ -121,6 +135,8 @@ router.get("/:id", requireAuth, async (req: Request, res: Response): Promise<voi
|
|||||||
router.post("/:id/share", requireAuth, async (req: Request, res: Response): Promise<void> => {
|
router.post("/:id/share", requireAuth, async (req: Request, res: Response): Promise<void> => {
|
||||||
const { user } = req as AuthenticatedRequest;
|
const { user } = req as AuthenticatedRequest;
|
||||||
const { userId, permission } = req.body as { userId?: string; permission?: "view" | "edit" };
|
const { userId, permission } = req.body as { userId?: string; permission?: "view" | "edit" };
|
||||||
|
const dbUserId = await resolveDbUserId(user.id);
|
||||||
|
if (!dbUserId) { res.status(401).json({ success: false, error: "USER_NOT_FOUND" }); return; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const playlist = await AppDataSource.getRepository(Playlist).findOneBy({ id: req.params.id });
|
const playlist = await AppDataSource.getRepository(Playlist).findOneBy({ id: req.params.id });
|
||||||
@@ -130,7 +146,7 @@ router.post("/:id/share", requireAuth, async (req: Request, res: Response): Prom
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playlist.ownerId !== user.id) {
|
if (playlist.ownerId !== dbUserId) {
|
||||||
res.status(403).json({ success: false, error: "FORBIDDEN" });
|
res.status(403).json({ success: false, error: "FORBIDDEN" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -165,11 +181,13 @@ router.patch("/:id/share/:shareId", requireAuth, async (req: Request, res: Respo
|
|||||||
permission?: "view" | "edit";
|
permission?: "view" | "edit";
|
||||||
status?: "active" | "revoked";
|
status?: "active" | "revoked";
|
||||||
};
|
};
|
||||||
|
const dbUserId = await resolveDbUserId(user.id);
|
||||||
|
if (!dbUserId) { res.status(401).json({ success: false, error: "USER_NOT_FOUND" }); return; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const playlist = await AppDataSource.getRepository(Playlist).findOneBy({ id: req.params.id });
|
const playlist = await AppDataSource.getRepository(Playlist).findOneBy({ id: req.params.id });
|
||||||
|
|
||||||
if (!playlist || playlist.ownerId !== user.id) {
|
if (!playlist || playlist.ownerId !== dbUserId) {
|
||||||
res.status(403).json({ success: false, error: "FORBIDDEN" });
|
res.status(403).json({ success: false, error: "FORBIDDEN" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
import { Router, Request, Response } from "express";
|
import { Router, Request, Response } from "express";
|
||||||
import { AppDataSource } from "../config/data-source";
|
import { AppDataSource } from "../config/data-source";
|
||||||
import { Video } from "../entities/Video";
|
import { Video } from "../entities/Video";
|
||||||
|
import { User } from "../entities/User";
|
||||||
import { requireAuth, AuthenticatedRequest, AuthenticatedUser } from "../middleware/auth.middleware";
|
import { requireAuth, AuthenticatedRequest, AuthenticatedUser } from "../middleware/auth.middleware";
|
||||||
import { UserSubscription } from "../entities/UserSubscription";
|
import { UserSubscription } from "../entities/UserSubscription";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
|
/** Résout le superOAuthId vers l'UUID DB, retourne null si user inconnu */
|
||||||
|
async function resolveDbUserId(superOAuthId: string): Promise<string | null> {
|
||||||
|
const user = await AppDataSource.getRepository(User).findOne({ where: { superOAuthId } });
|
||||||
|
return user?.id ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
/** Récupère le niveau de plan actif d'un user (0 = free si aucun abonnement actif) */
|
/** Récupère le niveau de plan actif d'un user (0 = free si aucun abonnement actif) */
|
||||||
async function getUserPlanLevel(userId: string): Promise<number> {
|
async function getUserPlanLevel(superOAuthId: string): Promise<number> {
|
||||||
|
const dbUserId = await resolveDbUserId(superOAuthId);
|
||||||
|
if (!dbUserId) return 0;
|
||||||
const sub = await AppDataSource.getRepository(UserSubscription).findOne({
|
const sub = await AppDataSource.getRepository(UserSubscription).findOne({
|
||||||
where: { userId, status: "active" },
|
where: { userId: dbUserId, status: "active" },
|
||||||
relations: ["plan"],
|
relations: ["plan"],
|
||||||
order: { startsAt: "DESC" },
|
order: { startsAt: "DESC" },
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user