chore: standardize security and quality foundations
Magent CI/CD / verify (push) Failing after 9m34s
Magent CI/CD / deploy-beta (push) Skipped

This commit is contained in:
2026-09-17 20:03:47 +12:00
parent 5639dbcb83
commit f852e7c941
127 changed files with 17928 additions and 10741 deletions
+39 -20
View File
@@ -1,24 +1,43 @@
export const FEATURES = [
{ key: 'stats', label: 'My Stats', description: 'View personal viewing history, reports and request report emails.' },
{ key: 'requests', label: 'My Requests', description: 'View existing requests, their progress and request actions.' },
{ key: 'new_requests', label: 'New Requests', description: 'Search for movies and TV shows and submit new requests.' },
{ key: 'issues', label: 'Issues', description: 'Report problems, follow up on issues and use available repair tools.' },
{ key: 'invites', label: 'Invites', description: 'Create and manage invitations within the existing invite limits.' },
{ key: 'ignore_profile_limits', label: 'Ignore profile limits', description: 'Allow explicit manual downloads outside quality, size, language or custom-format limits. Automatic searches keep the assigned profile.' },
] as const
export type Feature = typeof FEATURES[number]['key']
export type FeatureAccess = Record<Feature, boolean>
{ key: "stats", label: "My Stats", description: "View personal viewing history, reports and request report emails." },
{ key: "requests", label: "My Requests", description: "View existing requests, their progress and request actions." },
{
key: "new_requests",
label: "New Requests",
description: "Search for movies and TV shows and submit new requests.",
},
{
key: "issues",
label: "Issues",
description: "Report problems, follow up on issues and use available repair tools.",
},
{ key: "invites", label: "Invites", description: "Create and manage invitations within the existing invite limits." },
{
key: "ignore_profile_limits",
label: "Ignore profile limits",
description:
"Allow explicit manual downloads outside quality, size, language or custom-format limits. Automatic searches keep the assigned profile.",
},
] as const;
export type Feature = (typeof FEATURES)[number]["key"];
export type FeatureAccess = Record<Feature, boolean>;
export function featureForPath(path: string): Feature | undefined {
if (path === '/insights' || path.startsWith('/insights/')) return 'stats'
if (path === '/' || path.startsWith('/requests/')) return 'requests'
if (path === '/new-requests') return 'new_requests'
if (path.startsWith('/issues/confirm/') || path.startsWith('/portal/issues')) return 'issues'
if (path.startsWith('/profile/invites')) return 'invites'
if (path === '/portal/requests') return 'requests'
if (path === "/insights" || path.startsWith("/insights/")) return "stats";
if (path === "/" || path.startsWith("/requests/")) return "requests";
if (path === "/new-requests") return "new_requests";
if (path.startsWith("/issues/confirm/") || path.startsWith("/portal/issues")) return "issues";
if (path.startsWith("/profile/invites")) return "invites";
if (path === "/portal/requests") return "requests";
}
export function canAccess(user: { role?: string; features?: Partial<FeatureAccess>; invite_management_enabled?: boolean } | null, feature?: Feature) {
if (!feature) return true
if (!user) return false
if (user.role === 'admin') return true
return user.features?.[feature] ?? (feature === 'invites' ? Boolean(user.invite_management_enabled) : feature !== 'ignore_profile_limits')
export function canAccess(
user: { role?: string; features?: Partial<FeatureAccess>; invite_management_enabled?: boolean } | null,
feature?: Feature,
) {
if (!feature) return true;
if (!user) return false;
if (user.role === "admin") return true;
return (
user.features?.[feature] ??
(feature === "invites" ? Boolean(user.invite_management_enabled) : feature !== "ignore_profile_limits")
);
}