Adding primary boolean to user roles and setting the value to true for 'user' role creation on signup etc.

This commit is contained in:
Bradley Shellnut 2024-03-19 20:37:01 -07:00
parent 82de351c03
commit 20c59d7ee5
10 changed files with 3094 additions and 27 deletions

View file

@ -0,0 +1 @@
ALTER TABLE "user_roles" ADD COLUMN "default" boolean DEFAULT false;

View file

@ -0,0 +1 @@
ALTER TABLE "user_roles" RENAME COLUMN "default" TO "primary";

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -43,6 +43,20 @@
"when": 1710366724519, "when": 1710366724519,
"tag": "0005_light_captain_marvel", "tag": "0005_light_captain_marvel",
"breakpoints": true "breakpoints": true
},
{
"idx": 6,
"version": "5",
"when": 1710905422967,
"tag": "0006_wild_stone_men",
"breakpoints": true
},
{
"idx": 7,
"version": "5",
"when": 1710905572670,
"tag": "0007_large_miss_america",
"breakpoints": true
} }
] ]
} }

View file

@ -102,7 +102,7 @@ export const actions: Actions = {
}); });
} }
add_user_to_role(user[0].id, 'user'); add_user_to_role(user[0].id, 'user', true);
await db.insert(collections).values({ await db.insert(collections).values({
user_id: user[0].id user_id: user[0].id
}); });

View file

@ -4,7 +4,7 @@
import { zodClient } from 'sveltekit-superforms/adapters'; import { zodClient } from 'sveltekit-superforms/adapters';
import { superForm } from 'sveltekit-superforms/client'; import { superForm } from 'sveltekit-superforms/client';
import * as flashModule from 'sveltekit-flash-message/client'; import * as flashModule from 'sveltekit-flash-message/client';
import { ChevronsUpDown } from "lucide-svelte"; import { ChevronsUpDown } from 'lucide-svelte';
import { Button } from '$components/ui/button'; import { Button } from '$components/ui/button';
import { Label } from '$components/ui/label'; import { Label } from '$components/ui/label';
import { Input } from '$components/ui/input'; import { Input } from '$components/ui/input';
@ -15,20 +15,20 @@
export let data; export let data;
const { form, errors, enhance } = superForm(data.form, { const { form, errors, enhance } = superForm(data.form, {
onSubmit: () => boredState.update((n) => ({ ...n, loading: true })), onSubmit: () => boredState.update((n) => ({ ...n, loading: true })),
onResult: () => boredState.update((n) => ({ ...n, loading: false })), onResult: () => boredState.update((n) => ({ ...n, loading: false })),
flashMessage: { flashMessage: {
module: flashModule, module: flashModule,
onError: ({ result, message }) => { onError: ({ result, message }) => {
const errorMessage = result.error.message; const errorMessage = result.error.message;
message.set({ type: 'error', message: errorMessage }); message.set({ type: 'error', message: errorMessage });
}, }
}, },
taintedMessage: null, taintedMessage: null,
validators: zodClient(signUpSchema), validators: zodClient(signUpSchema),
delayMs: 0, delayMs: 0
}); });
let collapsibleOpen = false; let collapsibleOpen = false;
</script> </script>
@ -41,17 +41,22 @@
<form method="POST" action="/sign-up" use:enhance> <form method="POST" action="/sign-up" use:enhance>
<h2>Signup for an account</h2> <h2>Signup for an account</h2>
<Label for="username">Username</Label> <Label for="username">Username</Label>
<Input type="text" id="username" class={$errors.username && "outline outline-destructive"} name="username" placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} /> <Input type="text" id="username" class={$errors.username && "outline outline-destructive"} name="username"
placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} />
{#if $errors.username} {#if $errors.username}
<p class="text-sm text-destructive">{$errors.username}</p> <p class="text-sm text-destructive">{$errors.username}</p>
{/if} {/if}
<Label for="password">Password</Label> <Label for="password">Password</Label>
<Input type="password" id="password" class={$errors.password && "outline outline-destructive"} name="password" placeholder="Password" autocomplete="new-password" data-invalid={$errors.password} bind:value={$form.password} /> <Input type="password" id="password" class={$errors.password && "outline outline-destructive"} name="password"
placeholder="Password" autocomplete="new-password" data-invalid={$errors.password}
bind:value={$form.password} />
{#if $errors.password} {#if $errors.password}
<p class="text-sm text-destructive">{$errors.password}</p> <p class="text-sm text-destructive">{$errors.password}</p>
{/if} {/if}
<Label for="confirm_password">Confirm Password</Label> <Label for="confirm_password">Confirm Password</Label>
<Input type="password" id="confirm_password" class={$errors.confirm_password && "outline outline-destructive"} name="confirm_password" placeholder="Confirm Password" autocomplete="new-password" data-invalid={$errors.confirm_password} bind:value={$form.confirm_password} /> <Input type="password" id="confirm_password" class={$errors.confirm_password && "outline outline-destructive"}
name="confirm_password" placeholder="Confirm Password" autocomplete="new-password"
data-invalid={$errors.confirm_password} bind:value={$form.confirm_password} />
{#if $errors.confirm_password} {#if $errors.confirm_password}
<p class="text-sm text-destructive">{$errors.confirm_password}</p> <p class="text-sm text-destructive">{$errors.confirm_password}</p>
{/if} {/if}
@ -68,7 +73,8 @@
<Collapsible.Content> <Collapsible.Content>
<div transition:slide|global={{ delay: 10, duration: 150, easing: quintIn }}> <div transition:slide|global={{ delay: 10, duration: 150, easing: quintIn }}>
<Label for="email">Email</Label> <Label for="email">Email</Label>
<Input type="email" id="email" class={$errors.email && "outline outline-destructive"} name="email" placeholder="Email" autocomplete="email" data-invalid={$errors.email} bind:value={$form.email} /> <Input type="email" id="email" class={$errors.email && "outline outline-destructive"} name="email"
placeholder="Email" autocomplete="email" data-invalid={$errors.email} bind:value={$form.email} />
{#if $errors.email} {#if $errors.email}
<p class="text-sm text-destructive">{$errors.email}</p> <p class="text-sm text-destructive">{$errors.email}</p>
{/if} {/if}
@ -77,7 +83,9 @@
<Collapsible.Content> <Collapsible.Content>
<div transition:slide|global={{ delay: 10, duration: 150, easing: quintIn }}> <div transition:slide|global={{ delay: 10, duration: 150, easing: quintIn }}>
<Label for="firstName">First Name</Label> <Label for="firstName">First Name</Label>
<Input type="text" id="firstName" class={$errors.firstName && "outline outline-destructive"} name="firstName" placeholder="First Name" autocomplete="given-name" data-invalid={$errors.firstName} bind:value={$form.firstName} /> <Input type="text" id="firstName" class={$errors.firstName && "outline outline-destructive"} name="firstName"
placeholder="First Name" autocomplete="given-name" data-invalid={$errors.firstName}
bind:value={$form.firstName} />
{#if $errors.firstName} {#if $errors.firstName}
<p class="text-sm text-destructive">{$errors.firstName}</p> <p class="text-sm text-destructive">{$errors.firstName}</p>
{/if} {/if}
@ -86,7 +94,9 @@
<Collapsible.Content> <Collapsible.Content>
<div transition:slide|global={{ delay: 10, duration: 150, easing: quintIn }}> <div transition:slide|global={{ delay: 10, duration: 150, easing: quintIn }}>
<Label for="firstName">Last Name</Label> <Label for="firstName">Last Name</Label>
<Input type="text" id="lastName" class={$errors.firstName && "outline outline-destructive"} name="lastName" placeholder="Last Name" autocomplete="family-name" data-invalid={$errors.lastName} bind:value={$form.lastName} /> <Input type="text" id="lastName" class={$errors.firstName && "outline outline-destructive"} name="lastName"
placeholder="Last Name" autocomplete="family-name" data-invalid={$errors.lastName}
bind:value={$form.lastName} />
{#if $errors.lastName} {#if $errors.lastName}
<p class="text-sm text-destructive">{$errors.lastName}</p> <p class="text-sm text-destructive">{$errors.lastName}</p>
{/if} {/if}
@ -101,11 +111,12 @@
<Alert.Root> <Alert.Root>
<Alert.Title level="h3">Heads up!</Alert.Title> <Alert.Title level="h3">Heads up!</Alert.Title>
<Alert.Description> <Alert.Description>
Without an email address, you won't be able to reset your password. Submit only if you are sure. You can always add this later. Without an email address, you won't be able to reset your password. Submit only if you are sure. You can
always add this later.
</Alert.Description> </Alert.Description>
</Alert.Root> </Alert.Root>
{/if} {/if}
</form> </form>
</div> </div>
<style lang="postcss"> <style lang="postcss">

View file

@ -77,6 +77,7 @@ export const user_roles = pgTable('user_roles', {
role_id: uuid('role_id') role_id: uuid('role_id')
.notNull() .notNull()
.references(() => roles.id, { onDelete: 'cascade' }), .references(() => roles.id, { onDelete: 'cascade' }),
primary: boolean('primary').default(false),
created_at: timestamp('created_at').notNull().defaultNow(), created_at: timestamp('created_at').notNull().defaultNow(),
updated_at: timestamp('updated_at').notNull().defaultNow() updated_at: timestamp('updated_at').notNull().defaultNow()
}); });

View file

@ -1,8 +1,8 @@
import db from "$lib/drizzle"; import db from '$lib/drizzle';
import { eq } from "drizzle-orm"; import { eq } from 'drizzle-orm';
import { roles, user_roles } from "../schema"; import { roles, user_roles } from '../schema';
export async function add_user_to_role(user_id: string, role_name: string) { export async function add_user_to_role(user_id: string, role_name: string, primary = false) {
// Find the role by its name // Find the role by its name
const role = await db.query.roles.findFirst({ const role = await db.query.roles.findFirst({
where: eq(roles.name, role_name) where: eq(roles.name, role_name)
@ -13,10 +13,9 @@ export async function add_user_to_role(user_id: string, role_name: string) {
} }
// Create a UserRole entry linking the user and the role // Create a UserRole entry linking the user and the role
const userRole = await db.insert(user_roles).values({ return await db.insert(user_roles).values({
user_id, user_id,
role_id: role.id role_id: role.id,
primary
}); });
return userRole;
} }

View file

@ -11,13 +11,13 @@ export function create_user(user: Users) {
export async function find_or_create_user(user: Users) { export async function find_or_create_user(user: Users) {
const existing_user = await db.query.users.findFirst({ const existing_user = await db.query.users.findFirst({
where: eq(users.username, user.username), where: eq(users.username, user.username)
}); });
if (existing_user) { if (existing_user) {
return existing_user; return existing_user;
} else { } else {
const new_user = await create_user(user); const new_user = await create_user(user);
add_user_to_role(new_user.id, 'user'); add_user_to_role(new_user.id, 'user', true);
return new_user; return new_user;
} }
} }