diff --git a/alembic/versions/0011_add_password_reset_tokens.py b/alembic/versions/0011_add_password_reset_tokens.py
new file mode 100644
index 0000000..02fee2b
--- /dev/null
+++ b/alembic/versions/0011_add_password_reset_tokens.py
@@ -0,0 +1,25 @@
+"""Add password_reset_tokens table."""
+
+from alembic import op
+
+revision = "0011"
+down_revision = "0010"
+
+
+def upgrade() -> None:
+ op.execute("""
+ CREATE TABLE password_reset_tokens (
+ id SERIAL PRIMARY KEY,
+ user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+ token_hash TEXT NOT NULL UNIQUE,
+ expires_at TIMESTAMPTZ NOT NULL,
+ used BOOLEAN NOT NULL DEFAULT FALSE,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+ )
+ """)
+ op.execute("CREATE INDEX ix_password_reset_tokens_token_hash ON password_reset_tokens (token_hash)")
+ op.execute("CREATE INDEX ix_password_reset_tokens_user_id ON password_reset_tokens (user_id)")
+
+
+def downgrade() -> None:
+ op.execute("DROP TABLE IF EXISTS password_reset_tokens")
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index 1b895ed..e76f7b8 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -22,6 +22,18 @@ const router = createRouter({
component: () => import("@/views/RegisterView.vue"),
meta: { public: true },
},
+ {
+ path: "/forgot-password",
+ name: "forgot-password",
+ component: () => import("@/views/ForgotPasswordView.vue"),
+ meta: { public: true },
+ },
+ {
+ path: "/reset-password",
+ name: "reset-password",
+ component: () => import("@/views/ResetPasswordView.vue"),
+ meta: { public: true },
+ },
{
path: "/notes",
name: "notes",
diff --git a/frontend/src/views/ForgotPasswordView.vue b/frontend/src/views/ForgotPasswordView.vue
new file mode 100644
index 0000000..2a69590
--- /dev/null
+++ b/frontend/src/views/ForgotPasswordView.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
+
+
+ Enter the email address associated with your account and we'll send you a link to reset your password.
+
+
+
+
+
+
If an account exists with that email address, you will receive a password reset link shortly.
+
Check your email and follow the instructions to reset your password.
+
+
+
+
+
+
+
+
diff --git a/frontend/src/views/LoginView.vue b/frontend/src/views/LoginView.vue
index 732307d..620b96e 100644
--- a/frontend/src/views/LoginView.vue
+++ b/frontend/src/views/LoginView.vue
@@ -69,6 +69,9 @@ async function handleSubmit() {
class="input"
/>
+
+ Forgot your password?
+
{{ error }}