mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-14 17:26:26 +01:00
✨ Protect routes and procedures
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
|||||||
IconUser,
|
IconUser,
|
||||||
IconUsers,
|
IconUsers,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
|
import { useSession } from 'next-auth/react';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
@@ -47,6 +48,9 @@ export const ManageLayout = ({ children }: ManageLayoutProps) => {
|
|||||||
const [burgerMenuOpen, { toggle: toggleBurgerMenu, close: closeBurgerMenu }] =
|
const [burgerMenuOpen, { toggle: toggleBurgerMenu, close: closeBurgerMenu }] =
|
||||||
useDisclosure(false);
|
useDisclosure(false);
|
||||||
|
|
||||||
|
const data = useSession();
|
||||||
|
const isAdmin = data.data?.user.isAdmin ?? false;
|
||||||
|
|
||||||
const navigationLinks = (
|
const navigationLinks = (
|
||||||
<>
|
<>
|
||||||
<NavLink
|
<NavLink
|
||||||
@@ -69,37 +73,43 @@ export const ManageLayout = ({ children }: ManageLayoutProps) => {
|
|||||||
component={Link}
|
component={Link}
|
||||||
href="/manage/boards"
|
href="/manage/boards"
|
||||||
/>
|
/>
|
||||||
<NavLink
|
|
||||||
label="Users"
|
{isAdmin && (
|
||||||
icon={
|
<>
|
||||||
<ThemeIcon size="md" variant="light" color="red">
|
<NavLink
|
||||||
<IconUser size="1rem" />
|
label="Users"
|
||||||
</ThemeIcon>
|
icon={
|
||||||
}
|
<ThemeIcon size="md" variant="light" color="red">
|
||||||
>
|
<IconUser size="1rem" />
|
||||||
<NavLink
|
</ThemeIcon>
|
||||||
icon={<IconUsers size="1rem" />}
|
}
|
||||||
label="Manage"
|
>
|
||||||
component={Link}
|
<NavLink
|
||||||
href="/manage/users"
|
icon={<IconUsers size="1rem" />}
|
||||||
/>
|
label="Manage"
|
||||||
<NavLink
|
component={Link}
|
||||||
icon={<IconMailForward size="1rem" />}
|
href="/manage/users"
|
||||||
label="Invites"
|
/>
|
||||||
component={Link}
|
<NavLink
|
||||||
href="/manage/users/invites"
|
icon={<IconMailForward size="1rem" />}
|
||||||
/>
|
label="Invites"
|
||||||
</NavLink>
|
component={Link}
|
||||||
<NavLink
|
href="/manage/users/invites"
|
||||||
label="Settings"
|
/>
|
||||||
icon={
|
</NavLink>
|
||||||
<ThemeIcon size="md" variant="light" color="red">
|
<NavLink
|
||||||
<IconSettings2 size="1rem" />
|
label="Settings"
|
||||||
</ThemeIcon>
|
icon={
|
||||||
}
|
<ThemeIcon size="md" variant="light" color="red">
|
||||||
component={Link}
|
<IconSettings2 size="1rem" />
|
||||||
href="/manage/settings"
|
</ThemeIcon>
|
||||||
/>
|
}
|
||||||
|
component={Link}
|
||||||
|
href="/manage/settings"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<NavLink
|
<NavLink
|
||||||
label="Help"
|
label="Help"
|
||||||
icon={
|
icon={
|
||||||
|
|||||||
@@ -25,10 +25,13 @@ import {
|
|||||||
IconStarFilled,
|
IconStarFilled,
|
||||||
IconTrash,
|
IconTrash,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
|
import { GetServerSideProps } from 'next';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||||
|
import { getServerAuthSession } from '~/server/auth';
|
||||||
import { sleep } from '~/tools/client/time';
|
import { sleep } from '~/tools/client/time';
|
||||||
|
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
||||||
import { api } from '~/utils/api';
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
const BoardsPage = () => {
|
const BoardsPage = () => {
|
||||||
@@ -196,4 +199,26 @@ const BoardsPage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||||
|
const session = await getServerAuthSession(ctx);
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
return {
|
||||||
|
notFound: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const translations = await getServerSideTranslations(
|
||||||
|
['common'],
|
||||||
|
ctx.locale,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...translations,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default BoardsPage;
|
export default BoardsPage;
|
||||||
|
|||||||
@@ -10,12 +10,15 @@ import {
|
|||||||
createStyles,
|
createStyles,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { IconArrowRight } from '@tabler/icons-react';
|
import { IconArrowRight } from '@tabler/icons-react';
|
||||||
|
import { GetServerSideProps } from 'next';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||||
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
||||||
|
import { getServerAuthSession } from '~/server/auth';
|
||||||
|
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
||||||
|
|
||||||
const ManagementPage = () => {
|
const ManagementPage = () => {
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
@@ -102,6 +105,28 @@ const ManagementPage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||||
|
const session = await getServerAuthSession(ctx);
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
return {
|
||||||
|
notFound: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const translations = await getServerSideTranslations(
|
||||||
|
['common'],
|
||||||
|
ctx.locale,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...translations,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default ManagementPage;
|
export default ManagementPage;
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
const useStyles = createStyles((theme) => ({
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { Text, Title } from '@mantine/core';
|
import { Text, Title } from '@mantine/core';
|
||||||
|
import { GetServerSideProps } from 'next';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||||
|
import { getServerAuthSession } from '~/server/auth';
|
||||||
|
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
||||||
|
|
||||||
const SettingsPage = () => {
|
const SettingsPage = () => {
|
||||||
return (
|
return (
|
||||||
@@ -15,4 +18,26 @@ const SettingsPage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||||
|
const session = await getServerAuthSession(ctx);
|
||||||
|
|
||||||
|
if (!session?.user.isAdmin) {
|
||||||
|
return {
|
||||||
|
notFound: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const translations = await getServerSideTranslations(
|
||||||
|
['common'],
|
||||||
|
ctx.locale,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...translations,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default SettingsPage;
|
export default SettingsPage;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
IconUser,
|
IconUser,
|
||||||
IconUserPlus,
|
IconUserPlus,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
|
import { GetServerSideProps } from 'next';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -23,6 +24,8 @@ import {
|
|||||||
createAccountSecurityStepValidationSchema,
|
createAccountSecurityStepValidationSchema,
|
||||||
} from '~/components/Manage/User/Create/security-step';
|
} from '~/components/Manage/User/Create/security-step';
|
||||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||||
|
import { getServerAuthSession } from '~/server/auth';
|
||||||
|
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
||||||
import { api } from '~/utils/api';
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
const CreateNewUserPage = () => {
|
const CreateNewUserPage = () => {
|
||||||
@@ -221,4 +224,26 @@ const CreateNewUserPage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||||
|
const session = await getServerAuthSession(ctx);
|
||||||
|
|
||||||
|
if (!session?.user.isAdmin) {
|
||||||
|
return {
|
||||||
|
notFound: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const translations = await getServerSideTranslations(
|
||||||
|
['common'],
|
||||||
|
ctx.locale,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...translations,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default CreateNewUserPage;
|
export default CreateNewUserPage;
|
||||||
|
|||||||
@@ -14,10 +14,13 @@ import {
|
|||||||
import { useDebouncedValue } from '@mantine/hooks';
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
import { openContextModal } from '@mantine/modals';
|
import { openContextModal } from '@mantine/modals';
|
||||||
import { IconPlus, IconTrash } from '@tabler/icons-react';
|
import { IconPlus, IconTrash } from '@tabler/icons-react';
|
||||||
|
import { GetServerSideProps } from 'next';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||||
|
import { getServerAuthSession } from '~/server/auth';
|
||||||
|
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
||||||
import { api } from '~/utils/api';
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
const ManageUsersPage = () => {
|
const ManageUsersPage = () => {
|
||||||
@@ -134,4 +137,26 @@ const ManageUsersPage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||||
|
const session = await getServerAuthSession(ctx);
|
||||||
|
|
||||||
|
if (!session?.user.isAdmin) {
|
||||||
|
return {
|
||||||
|
notFound: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const translations = await getServerSideTranslations(
|
||||||
|
['common'],
|
||||||
|
ctx.locale,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...translations,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default ManageUsersPage;
|
export default ManageUsersPage;
|
||||||
|
|||||||
@@ -12,10 +12,13 @@ import {
|
|||||||
import { modals } from '@mantine/modals';
|
import { modals } from '@mantine/modals';
|
||||||
import { IconPlus, IconTrash } from '@tabler/icons-react';
|
import { IconPlus, IconTrash } from '@tabler/icons-react';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import { GetServerSideProps } from 'next';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||||
|
import { getServerAuthSession } from '~/server/auth';
|
||||||
|
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
||||||
import { api } from '~/utils/api';
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
const ManageUserInvitesPage = () => {
|
const ManageUserInvitesPage = () => {
|
||||||
@@ -151,4 +154,26 @@ const useStyles = createStyles(() => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||||
|
const session = await getServerAuthSession(ctx);
|
||||||
|
|
||||||
|
if (!session?.user.isAdmin) {
|
||||||
|
return {
|
||||||
|
notFound: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const translations = await getServerSideTranslations(
|
||||||
|
['common'],
|
||||||
|
ctx.locale,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...translations,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default ManageUserInvitesPage;
|
export default ManageUserInvitesPage;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from '../trpc';
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
|
||||||
|
|
||||||
import { getFrontendConfig } from '~/tools/config/getFrontendConfig';
|
import { getFrontendConfig } from '~/tools/config/getFrontendConfig';
|
||||||
|
|
||||||
export const boardRouter = createTRPCRouter({
|
export const boardRouter = createTRPCRouter({
|
||||||
all: publicProcedure.query(async ({ ctx }) => {
|
all: protectedProcedure.query(async ({ ctx }) => {
|
||||||
const files = fs.readdirSync('./data/configs').filter((file) => file.endsWith('.json'));
|
const files = fs.readdirSync('./data/configs').filter((file) => file.endsWith('.json'));
|
||||||
|
|
||||||
const userSettings = await ctx.prisma.userSettings.findUniqueOrThrow({
|
const userSettings = await ctx.prisma.userSettings.findUniqueOrThrow({
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { generate } from 'generate-password';
|
import { generate } from 'generate-password';
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from "../trpc";
|
import { adminProcedure, createTRPCRouter } from '../trpc';
|
||||||
|
|
||||||
export const passwordRouter = createTRPCRouter({
|
export const passwordRouter = createTRPCRouter({
|
||||||
generate: publicProcedure.mutation(() => {
|
generate: adminProcedure.mutation(() => {
|
||||||
return generate({
|
return generate({
|
||||||
strict: true,
|
strict: true,
|
||||||
numbers: true,
|
numbers: true,
|
||||||
@@ -11,7 +11,7 @@ export const passwordRouter = createTRPCRouter({
|
|||||||
uppercase: true,
|
uppercase: true,
|
||||||
symbols: true,
|
symbols: true,
|
||||||
excludeSimilarCharacters: true,
|
excludeSimilarCharacters: true,
|
||||||
length: 16
|
length: 16,
|
||||||
})
|
});
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
@@ -11,7 +11,13 @@ import {
|
|||||||
} from '~/validations/user';
|
} from '~/validations/user';
|
||||||
|
|
||||||
import { COOKIE_COLOR_SCHEME_KEY, COOKIE_LOCALE_KEY } from '../../../../data/constants';
|
import { COOKIE_COLOR_SCHEME_KEY, COOKIE_LOCALE_KEY } from '../../../../data/constants';
|
||||||
import { TRPCContext, createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
|
import {
|
||||||
|
TRPCContext,
|
||||||
|
adminProcedure,
|
||||||
|
createTRPCRouter,
|
||||||
|
protectedProcedure,
|
||||||
|
publicProcedure,
|
||||||
|
} from '../trpc';
|
||||||
|
|
||||||
export const userRouter = createTRPCRouter({
|
export const userRouter = createTRPCRouter({
|
||||||
createAdminAccount: publicProcedure.input(signUpFormSchema).mutation(async ({ ctx, input }) => {
|
createAdminAccount: publicProcedure.input(signUpFormSchema).mutation(async ({ ctx, input }) => {
|
||||||
@@ -182,7 +188,7 @@ export const userRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
makeDefaultDashboard: publicProcedure
|
makeDefaultDashboard: protectedProcedure
|
||||||
.input(z.object({ board: z.string() }))
|
.input(z.object({ board: z.string() }))
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
await ctx.prisma.userSettings.update({
|
await ctx.prisma.userSettings.update({
|
||||||
@@ -195,7 +201,7 @@ export const userRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
all: publicProcedure
|
all: adminProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
limit: z.number().min(1).max(100).default(10),
|
limit: z.number().min(1).max(100).default(10),
|
||||||
@@ -236,11 +242,11 @@ export const userRouter = createTRPCRouter({
|
|||||||
countPages: Math.ceil(countUsers / limit),
|
countPages: Math.ceil(countUsers / limit),
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
create: publicProcedure.input(createNewUserSchema).mutation(async ({ ctx, input }) => {
|
create: adminProcedure.input(createNewUserSchema).mutation(async ({ ctx, input }) => {
|
||||||
await createUserInNotExist(ctx, input);
|
await createUserInNotExist(ctx, input);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
deleteUser: publicProcedure
|
deleteUser: adminProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
userId: z.string(),
|
userId: z.string(),
|
||||||
|
|||||||
Reference in New Issue
Block a user