mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-30 18:29:37 +02:00
Add user first and last name fields to signup form
This commit is contained in:
parent
372db59211
commit
ba6a5283fe
12 changed files with 273 additions and 10 deletions
2
migrations/0007_nervous_scalphunter.sql
Normal file
2
migrations/0007_nervous_scalphunter.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE "user" ADD COLUMN "first_name" text NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE "user" ADD COLUMN "last_name" text NOT NULL;
|
147
migrations/meta/0007_snapshot.json
Normal file
147
migrations/meta/0007_snapshot.json
Normal file
|
@ -0,0 +1,147 @@
|
|||
{
|
||||
"id": "2039600b-1f5f-4f37-84dd-bb40636855e7",
|
||||
"prevId": "b0849b3e-02e1-42e1-b07c-6fa613c98e82",
|
||||
"version": "5",
|
||||
"dialect": "pg",
|
||||
"tables": {
|
||||
"featuredAdventures": {
|
||||
"name": "featuredAdventures",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"location": {
|
||||
"name": "location",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"session": {
|
||||
"name": "session",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"session_user_id_user_id_fk": {
|
||||
"name": "session_user_id_user_id_fk",
|
||||
"tableFrom": "session",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"sharedAdventures": {
|
||||
"name": "sharedAdventures",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"data": {
|
||||
"name": "data",
|
||||
"type": "json",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"username": {
|
||||
"name": "username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"first_name": {
|
||||
"name": "first_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"last_name": {
|
||||
"name": "last_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"hashed_password": {
|
||||
"name": "hashed_password",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
|
@ -50,6 +50,13 @@
|
|||
"when": 1712105206127,
|
||||
"tag": "0006_melted_leech",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"version": "5",
|
||||
"when": 1712167204757,
|
||||
"tag": "0007_nervous_scalphunter",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { visitCount } from "$lib/utils/stores/visitCountStore";
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
import type { DatabaseUser } from "$lib/server/auth";
|
||||
async function goHome() {
|
||||
goto("/");
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ export const sharedAdventures = pgTable("sharedAdventures", {
|
|||
export const userTable = pgTable("user", {
|
||||
id: text("id").primaryKey(),
|
||||
username: text("username").notNull(),
|
||||
first_name: text("first_name").notNull(),
|
||||
last_name: text("last_name").notNull(),
|
||||
hashed_password: varchar("hashed_password").notNull(),
|
||||
});
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle";
|
||||
import { Lucia } from "lucia";
|
||||
import { Lucia, TimeSpan } from "lucia";
|
||||
import { dev } from "$app/environment";
|
||||
import { userTable, sessionTable } from "$lib/db/schema";
|
||||
import { db } from "$lib/db/db.server";
|
||||
import { Argon2id } from "oslo/password";
|
||||
|
||||
const adapter = new DrizzlePostgreSQLAdapter(db, sessionTable, userTable);
|
||||
|
||||
|
@ -17,6 +16,9 @@ export const lucia = new Lucia(adapter, {
|
|||
return {
|
||||
// attributes has the type of DatabaseUserAttributes
|
||||
username: attributes.username,
|
||||
id: attributes.id,
|
||||
first_name: attributes.first_name,
|
||||
last_name: attributes.last_name,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@ -24,15 +26,14 @@ export const lucia = new Lucia(adapter, {
|
|||
declare module "lucia" {
|
||||
interface Register {
|
||||
Lucia: typeof lucia;
|
||||
DatabaseUserAttributes: DatabaseUserAttributes;
|
||||
DatabaseUserAttributes: DatabaseUser;
|
||||
}
|
||||
}
|
||||
|
||||
interface DatabaseUserAttributes {
|
||||
username: string;
|
||||
}
|
||||
export interface DatabaseUser {
|
||||
id: string;
|
||||
username: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
hashed_password: string;
|
||||
}
|
||||
|
|
12
src/routes/+layout.server.ts
Normal file
12
src/routes/+layout.server.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import type { LayoutServerLoad, PageServerLoad } from "./$types";
|
||||
|
||||
export const load: LayoutServerLoad = async (event) => {
|
||||
if (event.locals.user) {
|
||||
return {
|
||||
user: event.locals.user,
|
||||
};
|
||||
}
|
||||
return {
|
||||
user: null,
|
||||
};
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import Footer from "$lib/components/Footer.svelte";
|
||||
import Navbar from "$lib/components/Navbar.svelte";
|
||||
import "../app.css";
|
||||
|
|
30
src/routes/+page.server.ts
Normal file
30
src/routes/+page.server.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { lucia } from "$lib/server/auth";
|
||||
import { fail, redirect } from "@sveltejs/kit";
|
||||
|
||||
import type { Actions, PageServerLoad } from "./$types";
|
||||
|
||||
export const load: PageServerLoad = async (event) => {
|
||||
if (event.locals.user)
|
||||
return {
|
||||
user: event.locals.user,
|
||||
};
|
||||
return {
|
||||
user: null,
|
||||
};
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
if (!event.locals.session) {
|
||||
return fail(401);
|
||||
}
|
||||
|
||||
await lucia.invalidateSession(event.locals.session.id);
|
||||
const sessionCookie = lucia.createBlankSessionCookie();
|
||||
event.cookies.set(sessionCookie.name, sessionCookie.value, {
|
||||
path: ".",
|
||||
...sessionCookie.attributes,
|
||||
});
|
||||
return redirect(302, "/login");
|
||||
},
|
||||
};
|
|
@ -1,4 +1,8 @@
|
|||
<script lang="ts">
|
||||
import { enhance } from "$app/forms";
|
||||
import type { PageData } from "./$types";
|
||||
|
||||
export let data: PageData;
|
||||
import { goto } from "$app/navigation";
|
||||
import campingDrawing from "$lib/assets/camping.svg";
|
||||
import { visitCount } from "$lib/utils/stores/visitCountStore";
|
||||
|
@ -10,6 +14,9 @@
|
|||
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<article class="prose">
|
||||
{#if data.user && data.user.username != ""}
|
||||
<h1 class="mb-4">Welcome {data.user.first_name}. Let's get Exploring!</h1>
|
||||
{/if}
|
||||
<h1 class="mb-4">Welcome. Let's get Exploring!</h1>
|
||||
</article>
|
||||
<img src={campingDrawing} class="w-1/4 mb-4" alt="Logo" />
|
||||
|
@ -23,3 +30,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if data.user}
|
||||
<form method="post" use:enhance>
|
||||
<button>Sign out</button>
|
||||
</form>
|
||||
{/if}
|
||||
|
|
|
@ -4,17 +4,29 @@ import { fail, redirect } from "@sveltejs/kit";
|
|||
import { generateId } from "lucia";
|
||||
import { Argon2id } from "oslo/password";
|
||||
import { db } from "$lib/db/db.server";
|
||||
import type { DatabaseUser } from "$lib/server/auth";
|
||||
|
||||
import type { Actions } from "./$types";
|
||||
import { userTable } from "$lib/db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
const formData = await event.request.formData();
|
||||
const username = formData.get("username");
|
||||
const password = formData.get("password");
|
||||
const firstName = formData.get("first_name");
|
||||
const lastName = formData.get("last_name");
|
||||
// username must be between 4 ~ 31 characters, and only consists of lowercase letters, 0-9, -, and _
|
||||
// keep in mind some database (e.g. mysql) are case insensitive
|
||||
|
||||
// check all to make sure all fields are provided
|
||||
if (!username || !password || !firstName || !lastName) {
|
||||
return fail(400, {
|
||||
message: "All fields are required",
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
typeof username !== "string" ||
|
||||
username.length < 3 ||
|
||||
|
@ -35,17 +47,50 @@ export const actions: Actions = {
|
|||
});
|
||||
}
|
||||
|
||||
if (
|
||||
typeof firstName !== "string" ||
|
||||
firstName.length < 1 ||
|
||||
firstName.length > 255
|
||||
) {
|
||||
return fail(400, {
|
||||
message: "Invalid first name",
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
typeof lastName !== "string" ||
|
||||
lastName.length < 1 ||
|
||||
lastName.length > 255
|
||||
) {
|
||||
return fail(400, {
|
||||
message: "Invalid last name",
|
||||
});
|
||||
}
|
||||
|
||||
const userId = generateId(15);
|
||||
const hashedPassword = await new Argon2id().hash(password);
|
||||
|
||||
// TODO: check if username is already used
|
||||
const usernameTaken = await db
|
||||
.select()
|
||||
.from(userTable)
|
||||
.where(eq(userTable.username, username))
|
||||
.limit(1)
|
||||
.then((results) => results[0] as unknown as DatabaseUser | undefined);
|
||||
|
||||
if (usernameTaken) {
|
||||
return fail(400, {
|
||||
message: "Username already taken",
|
||||
});
|
||||
}
|
||||
await db
|
||||
.insert(userTable)
|
||||
.values({
|
||||
id: userId,
|
||||
username: username,
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
hashed_password: hashedPassword,
|
||||
})
|
||||
} as DatabaseUser)
|
||||
.execute();
|
||||
|
||||
const session = await lucia.createSession(userId, {});
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
<h1>Sign up</h1>
|
||||
<form method="post" use:enhance>
|
||||
<label for="username">Username</label>
|
||||
<label for="first_name">First Name</label>
|
||||
<input name="first_name" id="first_name" /><br />
|
||||
<label for="last_name">Last Name</label>
|
||||
<input name="last_name" id="last_name" /><br />
|
||||
<input name="username" id="username" /><br />
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="password" id="password" /><br />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue