mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Blocking admin pages if you don't already have an admin role. Adding add and remove roles to admin page.
This commit is contained in:
parent
5e174c875f
commit
66eb09b237
8 changed files with 153 additions and 57 deletions
|
|
@ -1 +1,5 @@
|
|||
export const notSignedInMessage = { type: 'error', message: 'You are not signed in' } as const;
|
||||
export const notSignedInMessage = { type: 'error', message: 'You are not signed in' } as const;
|
||||
export const forbiddenMessage = {
|
||||
type: 'error',
|
||||
message: 'You are not allowed to access this'
|
||||
} as const;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { redirect } from 'sveltekit-flash-message/server';
|
||||
import { notSignedInMessage } from '$lib/flashMessages';
|
||||
import db from '$lib/drizzle';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import db from '$lib/drizzle';
|
||||
import { user_roles } from '../../../../schema';
|
||||
|
||||
export async function load(event) {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import { eq, inArray, not } from 'drizzle-orm';
|
||||
import { and, eq, inArray, not } from 'drizzle-orm';
|
||||
import { redirect } from 'sveltekit-flash-message/server';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { notSignedInMessage } from '$lib/flashMessages';
|
||||
import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages';
|
||||
import db from '$lib/drizzle';
|
||||
import { roles, users } from '../../../../../../schema';
|
||||
import { roles, user_roles, users } from '../../../../../../schema';
|
||||
|
||||
export const load: PageServerLoad = async (event) => {
|
||||
const { params } = event;
|
||||
const { id } = params;
|
||||
const { user } = event.locals;
|
||||
|
||||
// TODO: Ensure admin user
|
||||
if (!event.locals.user) {
|
||||
|
|
@ -30,10 +31,15 @@ export const load: PageServerLoad = async (event) => {
|
|||
}
|
||||
});
|
||||
|
||||
const containsAdminRole = foundUser?.user_roles?.some(
|
||||
(user_role) => user_role?.role?.name === 'admin'
|
||||
);
|
||||
if (!containsAdminRole) {
|
||||
console.log('Not an admin');
|
||||
redirect(302, '/login', notSignedInMessage, event);
|
||||
}
|
||||
|
||||
const currentRoleIds = foundUser?.user_roles?.map((user_role) => user_role?.role.cuid) || [];
|
||||
|
||||
console.log('currentRoleIds', currentRoleIds);
|
||||
|
||||
let availableRoles: { name: string; cuid: string }[] = [];
|
||||
if (currentRoleIds?.length > 0) {
|
||||
availableRoles = await db.query.roles.findMany({
|
||||
|
|
@ -53,13 +59,83 @@ export const load: PageServerLoad = async (event) => {
|
|||
|
||||
export const actions = {
|
||||
addRole: async (event) => {
|
||||
const { params, request } = event;
|
||||
d;
|
||||
const data = await request.formData();
|
||||
console.log('data', data);
|
||||
const { request, locals } = event;
|
||||
const { user } = locals;
|
||||
|
||||
const roleCUID = data.get('value');
|
||||
const dbRole = await db.query.roles.findFirst({ where: eq(roles.cuid, roleCUID?.toString()) });
|
||||
if (!user) {
|
||||
redirect(302, '/login', notSignedInMessage, event);
|
||||
}
|
||||
|
||||
const userRoles = await db.query.user_roles.findMany({
|
||||
where: eq(users.id, user.id),
|
||||
with: {
|
||||
role: {
|
||||
columns: {
|
||||
name: true,
|
||||
cuid: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const containsAdminRole = userRoles.some((user_role) => user_role?.role?.name === 'admin');
|
||||
if (!containsAdminRole) {
|
||||
redirect(302, '/login', forbiddenMessage, event);
|
||||
}
|
||||
|
||||
const data = await request.formData();
|
||||
const role = data.get('role');
|
||||
const dbRole = await db.query.roles.findFirst({
|
||||
where: eq(roles.cuid, role?.toString() ?? '')
|
||||
});
|
||||
console.log('dbRole', dbRole);
|
||||
if (dbRole) {
|
||||
await db.insert(user_roles).values({
|
||||
user_id: user.id,
|
||||
role_id: dbRole.id
|
||||
});
|
||||
return {
|
||||
success: true
|
||||
};
|
||||
}
|
||||
},
|
||||
removeRole: async (event) => {
|
||||
const { request, locals } = event;
|
||||
const { user } = locals;
|
||||
if (!user) {
|
||||
redirect(302, '/login', notSignedInMessage, event);
|
||||
}
|
||||
|
||||
const userRoles = await db.query.user_roles.findMany({
|
||||
where: eq(users.id, user.id),
|
||||
with: {
|
||||
role: {
|
||||
columns: {
|
||||
name: true,
|
||||
cuid: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const containsAdminRole = userRoles.some((user_role) => user_role?.role?.name === 'admin');
|
||||
if (!containsAdminRole) {
|
||||
redirect(302, '/login', forbiddenMessage, event);
|
||||
}
|
||||
|
||||
const data = await request.formData();
|
||||
const role = data.get('role');
|
||||
const dbRole = await db.query.roles.findFirst({
|
||||
where: eq(roles.cuid, role?.toString() ?? '')
|
||||
});
|
||||
console.log('dbRole', dbRole);
|
||||
if (dbRole) {
|
||||
await db
|
||||
.delete(user_roles)
|
||||
.where(and(eq(user_roles.user_id, user.id), eq(user_roles.role_id, dbRole.id)));
|
||||
return {
|
||||
success: true
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
// import AddRolesForm from './add-roles-form.svelte';
|
||||
|
||||
export let data;
|
||||
export let form;
|
||||
|
||||
const { user, availableRoles } = data;
|
||||
const { user_roles }: { user_roles: { role: { name: string, cuid: string } }[] } = user;
|
||||
</script>
|
||||
|
|
@ -17,15 +19,29 @@
|
|||
|
||||
<h2>User Roles</h2>
|
||||
{#each user_roles as user_role}
|
||||
<p>{capitalize(user_role?.role?.name)}</p>
|
||||
{#if user_role?.role?.name !== 'user'}
|
||||
<form action="?/removeRole" method="POST" use:enhance data-sveltekit-reload>
|
||||
<div class="flex flex-row space-x-3 place-items-center mt-2">
|
||||
<input id="role" type="hidden" name="role" value={user_role?.role?.cuid} />
|
||||
<Button type="submit">Remove</Button>
|
||||
<p>{capitalize(user_role?.role?.name)}</p>
|
||||
</div>
|
||||
</form>
|
||||
{:else}
|
||||
<p>{capitalize(user_role?.role?.name)}</p>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
{#if form?.success}
|
||||
<p>Sucessfully added role</p>
|
||||
{/if}
|
||||
|
||||
<h2>Roles Available to Assign</h2>
|
||||
<!--<AddRolesForm {availableRoles} />-->
|
||||
{#each availableRoles as role}
|
||||
<form action="?/addRole" method="POST" use:enhance>
|
||||
<form action="?/addRole" method="POST" use:enhance data-sveltekit-reload>
|
||||
<div class="flex flex-row space-x-3 place-items-center mt-2">
|
||||
<input type="hidden" name="role" value={role?.cuid} />
|
||||
<input id="role" type="hidden" name="role" value={role?.cuid} />
|
||||
<Button type="submit">Add</Button>
|
||||
<p>{capitalize(role?.name)}</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
<script lang="ts">
|
||||
import * as Form from "$lib/components/ui/form";
|
||||
import { Input } from "$lib/components/ui/input";
|
||||
import { Checkbox } from "$lib/components/ui/checkbox/index.js";
|
||||
import * as Form from '$lib/components/ui/form';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Checkbox } from '$lib/components/ui/checkbox/index.js';
|
||||
import { addRoleSchema, type AddRoleSchema } from '$lib/validations/account';
|
||||
import {
|
||||
type SuperValidated,
|
||||
type Infer,
|
||||
superForm,
|
||||
} from "sveltekit-superforms";
|
||||
import { zodClient } from "sveltekit-superforms/adapters";
|
||||
superForm
|
||||
} from 'sveltekit-superforms';
|
||||
import { zodClient } from 'sveltekit-superforms/adapters';
|
||||
|
||||
export let availableRoles: { name: string; cuid: string }[] = [];
|
||||
const data: SuperValidated<Infer<AddRoleSchema>> = availableRoles;
|
||||
|
||||
const form = superForm(data, {
|
||||
validators: zodClient(addRoleSchema),
|
||||
validators: zodClient(addRoleSchema)
|
||||
// onUpdated: ({ form: f }) => {
|
||||
// if (f.valid) {
|
||||
// toast.success("You submitted" + JSON.stringify(f.data, null, 2));
|
||||
|
|
|
|||
|
|
@ -1,37 +1,37 @@
|
|||
<script lang="ts">
|
||||
import Ellipsis from "lucide-svelte/icons/ellipsis";
|
||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import Ellipsis from 'lucide-svelte/icons/ellipsis';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { User } from 'lucide-svelte';
|
||||
|
||||
export let cuid: string;
|
||||
export let cuid: string;
|
||||
</script>
|
||||
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild let:builder>
|
||||
<Button
|
||||
variant="ghost"
|
||||
builders={[builder]}
|
||||
size="icon"
|
||||
class="relative h-8 w-8 p-0"
|
||||
>
|
||||
<span class="sr-only">Open menu</span>
|
||||
<Ellipsis class="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Label>Actions</DropdownMenu.Label>
|
||||
<DropdownMenu.Item on:click={() => navigator.clipboard.writeText(cuid)}>
|
||||
Copy User ID
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Trigger asChild let:builder>
|
||||
<Button
|
||||
variant="ghost"
|
||||
builders={[builder]}
|
||||
size="icon"
|
||||
class="relative h-8 w-8 p-0"
|
||||
>
|
||||
<span class="sr-only">Open menu</span>
|
||||
<Ellipsis class="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Label>Actions</DropdownMenu.Label>
|
||||
<DropdownMenu.Item on:click={() => navigator.clipboard.writeText(cuid)}>
|
||||
Copy User ID
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Separator />
|
||||
<a href={`/admin/users/${cuid}`}>
|
||||
<DropdownMenu.Item>
|
||||
<User class="mr-2 h-4 w-4" />
|
||||
<span>View user</span>
|
||||
</DropdownMenu.Item>
|
||||
</a>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<script lang="ts">
|
||||
import { Checkbox } from "$lib/components/ui/checkbox";
|
||||
import type { Writable } from "svelte/store";
|
||||
import { Checkbox } from '$lib/components/ui/checkbox';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
export let checked: Writable<boolean>;
|
||||
export let checked: Writable<boolean>;
|
||||
</script>
|
||||
|
||||
<Checkbox bind:checked={$checked} />
|
||||
|
|
@ -29,12 +29,12 @@ const signUpDefaults = {
|
|||
};
|
||||
|
||||
export const load: PageServerLoad = async (event) => {
|
||||
redirect(
|
||||
302,
|
||||
'/waitlist',
|
||||
{ type: 'error', message: 'Sign-up not yet available. Please add your email to the waitlist!' },
|
||||
event
|
||||
);
|
||||
// redirect(
|
||||
// 302,
|
||||
// '/waitlist',
|
||||
// { type: 'error', message: 'Sign-up not yet available. Please add your email to the waitlist!' },
|
||||
// event
|
||||
// );
|
||||
|
||||
if (event.locals.user) {
|
||||
const message = { type: 'success', message: 'You are already signed in' } as const;
|
||||
|
|
|
|||
Loading…
Reference in a new issue