mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-11 17:46:06 +00:00
172 lines
4.0 KiB
TypeScript
172 lines
4.0 KiB
TypeScript
import { notFound, redirect } from 'next/navigation';
|
|
import { auth, checkProjectAccess, checkWorkspaceAccess } from '@/lib/auth';
|
|
import { db } from '@/lib/db';
|
|
|
|
type AccessIntent = 'view' | 'manage';
|
|
|
|
const LOGIN_REDIRECT = '/login';
|
|
const FORBIDDEN_REDIRECT = '/dashboard';
|
|
|
|
function redirectForMissingAuth() {
|
|
redirect(LOGIN_REDIRECT);
|
|
}
|
|
|
|
function redirectForForbidden() {
|
|
redirect(FORBIDDEN_REDIRECT);
|
|
}
|
|
|
|
function ensureGuestPolicy(options: { userId?: string; intent: AccessIntent; allowPublicView: boolean }) {
|
|
const { userId, intent, allowPublicView } = options;
|
|
if (userId) return;
|
|
|
|
if (intent !== 'view' || !allowPublicView) {
|
|
redirectForMissingAuth();
|
|
}
|
|
}
|
|
|
|
async function assertProjectAccessOrRedirect(
|
|
project: { id: string; ownerId: string; workspaceId: string; visibility: string },
|
|
options: {
|
|
userId?: string;
|
|
intent: AccessIntent;
|
|
allowPublicView: boolean;
|
|
}
|
|
) {
|
|
const { userId, intent, allowPublicView } = options;
|
|
|
|
ensureGuestPolicy({ userId, intent, allowPublicView });
|
|
|
|
const access = await checkProjectAccess(project, userId, { intent });
|
|
|
|
if (!access.hasAccess) {
|
|
if (!userId) {
|
|
redirectForMissingAuth();
|
|
}
|
|
redirectForForbidden();
|
|
}
|
|
|
|
if (intent === 'manage' && !access.canEdit) {
|
|
redirectForForbidden();
|
|
}
|
|
|
|
return access;
|
|
}
|
|
|
|
export async function requireAuthOrRedirect() {
|
|
const session = await auth();
|
|
if (!session?.user?.id) {
|
|
redirectForMissingAuth();
|
|
}
|
|
return session;
|
|
}
|
|
|
|
export async function requireWorkspaceAccessOrRedirect(options: {
|
|
workspaceId: string;
|
|
userId?: string;
|
|
intent?: AccessIntent;
|
|
}) {
|
|
const { workspaceId, userId, intent = 'view' } = options;
|
|
const resolvedUserId = userId ?? (await auth())?.user?.id;
|
|
|
|
if (!resolvedUserId) {
|
|
redirectForMissingAuth();
|
|
}
|
|
|
|
const workspace = await db.workspace.findUnique({
|
|
where: { id: workspaceId },
|
|
select: { id: true, ownerId: true },
|
|
});
|
|
|
|
if (!workspace) {
|
|
notFound();
|
|
}
|
|
|
|
const access = await checkWorkspaceAccess(workspace, resolvedUserId);
|
|
|
|
if (!access.hasAccess) {
|
|
redirectForForbidden();
|
|
}
|
|
|
|
if (intent === 'manage' && !access.canEdit) {
|
|
redirectForForbidden();
|
|
}
|
|
|
|
return { workspace, access };
|
|
}
|
|
|
|
export async function requireProjectAccessOrRedirect(options: {
|
|
projectId: string;
|
|
userId?: string;
|
|
intent?: AccessIntent;
|
|
allowPublicView?: boolean;
|
|
}) {
|
|
const { projectId, userId, intent = 'view', allowPublicView = false } = options;
|
|
const resolvedUserId = userId ?? (await auth())?.user?.id;
|
|
|
|
// Fail closed before resource lookup when the route is not public.
|
|
if (!resolvedUserId && !allowPublicView) {
|
|
redirectForMissingAuth();
|
|
}
|
|
|
|
const project = await db.project.findUnique({
|
|
where: { id: projectId },
|
|
select: { id: true, ownerId: true, workspaceId: true, visibility: true },
|
|
});
|
|
|
|
if (!project) {
|
|
if (!resolvedUserId) {
|
|
redirectForMissingAuth();
|
|
}
|
|
notFound();
|
|
}
|
|
|
|
const access = await assertProjectAccessOrRedirect(project, {
|
|
userId: resolvedUserId,
|
|
intent,
|
|
allowPublicView,
|
|
});
|
|
|
|
return { project, access };
|
|
}
|
|
|
|
export async function requireVideoProjectAccessOrRedirect(options: {
|
|
projectId: string;
|
|
videoId: string;
|
|
userId?: string;
|
|
intent?: AccessIntent;
|
|
allowPublicView?: boolean;
|
|
}) {
|
|
const { projectId, videoId, userId, intent = 'view', allowPublicView = false } = options;
|
|
const resolvedUserId = userId ?? (await auth())?.user?.id;
|
|
|
|
// Fail closed before resource lookup when the route is not public.
|
|
if (!resolvedUserId && !allowPublicView) {
|
|
redirectForMissingAuth();
|
|
}
|
|
|
|
const video = await db.video.findFirst({
|
|
where: { id: videoId, projectId },
|
|
select: {
|
|
id: true,
|
|
project: {
|
|
select: { id: true, ownerId: true, workspaceId: true, visibility: true },
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!video) {
|
|
if (!resolvedUserId) {
|
|
redirectForMissingAuth();
|
|
}
|
|
notFound();
|
|
}
|
|
|
|
const access = await assertProjectAccessOrRedirect(video.project, {
|
|
userId: resolvedUserId,
|
|
intent,
|
|
allowPublicView,
|
|
});
|
|
|
|
return { video, access, project: video.project };
|
|
}
|