Add user authentication with login/register

- Add users and sessions tables to database schema
- Add bcryptjs for password hashing
- Create auth API routes (login, register, logout, me)
- Add AuthProvider context for client-side auth state
- Update all API routes to require authentication and filter by userId
- Create login and register pages
- Add AppShell component for authenticated layout
- Update all pages to use AppShell and show user info
- Each user now has their own private entries and tags

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Gemini Agent
2026-01-24 06:18:41 +00:00
parent 5555c1e6b5
commit 1455b0acd1
27 changed files with 1039 additions and 75 deletions

View File

@@ -1,25 +0,0 @@
CREATE TABLE `entries` (
`id` text PRIMARY KEY NOT NULL,
`date` text NOT NULL,
`text` text NOT NULL,
`mood` integer,
`rough_day` integer DEFAULT 0 NOT NULL,
`created_at` integer NOT NULL,
`updated_at` integer NOT NULL
);
--> statement-breakpoint
CREATE TABLE `entry_tags` (
`entry_id` text NOT NULL,
`tag_id` text NOT NULL,
PRIMARY KEY(`entry_id`, `tag_id`),
FOREIGN KEY (`entry_id`) REFERENCES `entries`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`tag_id`) REFERENCES `tags`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `tags` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`created_at` integer NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `tags_name_unique` ON `tags` (`name`);

View File

@@ -0,0 +1,45 @@
CREATE TABLE `entries` (
`id` text PRIMARY KEY NOT NULL,
`user_id` text NOT NULL,
`date` text NOT NULL,
`text` text NOT NULL,
`mood` integer,
`rough_day` integer DEFAULT 0 NOT NULL,
`created_at` integer NOT NULL,
`updated_at` integer NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `entry_tags` (
`entry_id` text NOT NULL,
`tag_id` text NOT NULL,
PRIMARY KEY(`entry_id`, `tag_id`),
FOREIGN KEY (`entry_id`) REFERENCES `entries`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`tag_id`) REFERENCES `tags`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `sessions` (
`id` text PRIMARY KEY NOT NULL,
`user_id` text NOT NULL,
`expires_at` integer NOT NULL,
`created_at` integer NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `tags` (
`id` text PRIMARY KEY NOT NULL,
`user_id` text NOT NULL,
`name` text NOT NULL,
`created_at` integer NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `users` (
`id` text PRIMARY KEY NOT NULL,
`email` text NOT NULL,
`password_hash` text NOT NULL,
`name` text,
`created_at` integer NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);

View File

@@ -1,7 +1,7 @@
{
"version": "6",
"dialect": "sqlite",
"id": "5116dd80-0292-4345-a403-fd678d148880",
"id": "597c7e60-5f2b-4b3c-befb-4c929ccbc227",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"entries": {
@@ -14,6 +14,13 @@
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"date": {
"name": "date",
"type": "text",
@@ -59,7 +66,21 @@
}
},
"indexes": {},
"foreignKeys": {},
"foreignKeys": {
"entries_user_id_users_id_fk": {
"name": "entries_user_id_users_id_fk",
"tableFrom": "entries",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
@@ -123,6 +144,58 @@
"uniqueConstraints": {},
"checkConstraints": {}
},
"sessions": {
"name": "sessions",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expires_at": {
"name": "expires_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"tags": {
"name": "tags",
"columns": {
@@ -133,6 +206,13 @@
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
@@ -148,11 +228,70 @@
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"tags_user_id_users_id_fk": {
"name": "tags_user_id_users_id_fk",
"tableFrom": "tags",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"password_hash": {
"name": "password_hash",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"tags_name_unique": {
"name": "tags_name_unique",
"users_email_unique": {
"name": "users_email_unique",
"columns": [
"name"
"email"
],
"isUnique": true
}

View File

@@ -5,8 +5,8 @@
{
"idx": 0,
"version": "6",
"when": 1769219562573,
"tag": "0000_busy_earthquake",
"when": 1769235451953,
"tag": "0000_flat_captain_marvel",
"breakpoints": true
}
]