Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/(auth)/budgets/edit/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import BudgetForm from "@/components/budgets/form";
import { getCategories, getBudgetById } from "@/lib/dal";
import { getCategories, getBudgetById } from "@/lib/data";
import type { Metadata } from "next";
import { notFound } from "next/navigation";

Expand Down
2 changes: 1 addition & 1 deletion app/(auth)/budgets/new/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import BudgetForm from "@/components/budgets/form";
import { getCategories } from "@/lib/dal";
import { getCategories } from "@/lib/data";
import type { Metadata } from "next";

export const metadata: Metadata = {
Expand Down
2 changes: 1 addition & 1 deletion app/(auth)/expenses/edit/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ExpenseForm from "@/components/expenses/form";
import { getCategories, getExpenseById } from "@/lib/dal";
import { getCategories, getExpenseById } from "@/lib/data";
import type { Metadata } from "next";
import { notFound } from "next/navigation";

Expand Down
2 changes: 1 addition & 1 deletion app/(auth)/expenses/new/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ExpenseForm from "@/components/expenses/form";
import { getCategories } from "@/lib/dal";
import { getCategories } from "@/lib/data";
import type { Metadata } from "next";

export const metadata: Metadata = {
Expand Down
2 changes: 1 addition & 1 deletion app/(auth)/expenses/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import ExpensesListWrapper from "@/components/expenses/list-wrapper";
import { Suspense } from "react";
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card";
import SearchBar from "@/components/expenses/search-bar";
import { getExpensesPages } from "@/lib/dal";
import { getExpensesPages } from "@/lib/data";
import PaginationControls from "@/components/pagination-controls";
import ExpensesSkeleton from "@/components/skeletons/expenses";

Expand Down
33 changes: 23 additions & 10 deletions app/actions/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import {
type SignupInput,
type SigninInput
} from "@/lib/auth/schemas";
import { createSession, deleteSession } from "@/lib/auth/session";
import { deleteSession, createSession } from "@/lib/auth/session";
import prisma from "@/lib/prisma";
import bcrypt from "bcryptjs";
import { revokeRefreshToken } from "@/lib/data";
import { redirect } from "next/navigation";
import { cookies } from "next/headers";
import type { ActionResponse } from "@/types";
import { checkPasswordHash, hashPassword } from "@/lib/auth/password";

export async function signup(data: SignupInput): Promise<ActionResponse> {
const validatedFields = signupSchema.safeParse(data);
Expand All @@ -35,7 +37,7 @@ export async function signup(data: SignupInput): Promise<ActionResponse> {
};
}

const hashedPassword = await bcrypt.hash(password, 10);
const hashedPassword = await hashPassword(password);

const user = await prisma.user.create({
data: {
Expand All @@ -49,15 +51,15 @@ export async function signup(data: SignupInput): Promise<ActionResponse> {
});

await createSession(user.id);

return { success: true };
} catch (error) {
console.error("Signup error:", error);
return {
success: false,
message: "An error occurred while creating your account"
};
}

redirect("/dashboard");
}

export async function login(data: SigninInput): Promise<ActionResponse> {
Expand All @@ -82,7 +84,7 @@ export async function login(data: SigninInput): Promise<ActionResponse> {
};
}

const isMatch = await bcrypt.compare(password, user.password.hash);
const isMatch = await checkPasswordHash(password, user.password.hash);

if (!isMatch) {
return {
Expand All @@ -92,15 +94,26 @@ export async function login(data: SigninInput): Promise<ActionResponse> {
}

await createSession(user.id);

return { success: true };
} catch (error) {
console.error(error);
return { success: false, message: "An error occured during login" };
}

redirect("/dashboard");
}

export async function logout() {
await deleteSession();
redirect("/login");
const cookieStore = await cookies();

try {
const refreshToken = cookieStore.get("refresh_token")?.value;
if (refreshToken) {
await revokeRefreshToken(refreshToken);
}
} catch (error) {
console.error(error);
} finally {
await deleteSession();
redirect("/login");
}
}
12 changes: 3 additions & 9 deletions app/actions/budget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

import prisma from "@/lib/prisma";
import { budgetSchema, type BudgetInput } from "@/lib/budget/schemas";
import { verifySession } from "@/lib/dal";
import { verifySession } from "@/lib/auth/session";
import type { ActionResponse, Budget } from "@/types";
import { BudgetType } from "@/app/generated/prisma";

export async function createBudget(data: BudgetInput): Promise<ActionResponse> {
const session = await verifySession();
const validatedFields = budgetSchema.safeParse(data);

if (!session || !validatedFields.success) {
if (!validatedFields.success) {
return {
success: false
};
Expand Down Expand Up @@ -76,7 +76,7 @@ export async function updateBudget(
const session = await verifySession();
const validatedFields = budgetSchema.safeParse(data);

if (!session || !validatedFields.success) {
if (!validatedFields.success) {
return {
success: false
};
Expand All @@ -103,12 +103,6 @@ export async function updateBudget(
export async function deleteBudget(id: Budget["id"]): Promise<ActionResponse> {
const session = await verifySession();

if (!session) {
return {
success: false
};
}

try {
await prisma.budget.delete({
where: { id, userId: session.userId }
Expand Down
12 changes: 3 additions & 9 deletions app/actions/expense.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
"use server";

import prisma from "@/lib/prisma";
import { verifySession } from "@/lib/dal";
import { verifySession } from "@/lib/auth/session";
import { type ExpenseInput, expenseSchema } from "@/lib/expense/schemas";
import type { ActionResponse, Expense } from "@/types";

export async function createExpense(data: ExpenseInput): Promise<ActionResponse> {
const session = await verifySession();
const validatedFields = expenseSchema.safeParse(data);

if (!session || !validatedFields.success) {
if (!validatedFields.success) {
return {
success: false
};
Expand Down Expand Up @@ -66,7 +66,7 @@ export async function updateExpense(
const session = await verifySession();
const validatedFields = expenseSchema.safeParse(data);

if (!session || !validatedFields.success) {
if (!validatedFields.success) {
return {
success: false
};
Expand Down Expand Up @@ -121,12 +121,6 @@ export async function updateExpense(
export async function deleteExpense(id: Expense["id"]): Promise<ActionResponse> {
const session = await verifySession();

if (!session) {
return {
success: false
};
}

try {
await prisma.expense.delete({
where: { id, userId: session.userId }
Expand Down
2 changes: 1 addition & 1 deletion components/budgets/container.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import BudgetsList from "@/components/budgets/list";
import EmptyList from "@/components/empty-list";
import { Card, CardContent } from "@/components/ui/card";
import { getBudgets, getExpensesByCategory } from "@/lib/dal";
import { getBudgets, getExpensesByCategory } from "@/lib/data";
import { getCurrentMonthRange } from "@/lib/utils";

export async function BudgetsContainer() {
Expand Down
2 changes: 1 addition & 1 deletion components/dashboard/data-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ExpensesList from "@/components/expenses/list";
import Chart from "@/components/dashboard/chart";
import EmptyList from "@/components/empty-list";
import ChartHeader from "@/components/dashboard/chart-header";
import { getDashboardData } from "@/lib/dashboard-data";
import { getDashboardData } from "@/lib/dashboard";

export default async function DashboardDataContainer({
params
Expand Down
2 changes: 1 addition & 1 deletion components/expenses/list-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getPaginatedExpenses } from "@/lib/dal";
import { getPaginatedExpenses } from "@/lib/data";
import EmptyList from "@/components/empty-list";
import ExpensesList from "@/components/expenses/list";

Expand Down
6 changes: 1 addition & 5 deletions components/login-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ import { type SigninInput, signinSchema } from "@/lib/auth/schemas";
import { login } from "@/app/actions/auth";
import { useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import FormAlert from "@/components/form-alert";

export default function LoginForm() {
const router = useRouter();
const [serverError, setServerError] = useState<string | null>(null);
const form = useForm<SigninInput>({
resolver: zodResolver(signinSchema),
Expand All @@ -37,9 +35,7 @@ export default function LoginForm() {
setServerError(null);
const result = await login(data);

if (result.success) {
router.push("/dashboard");
} else {
if (!result.success) {
setServerError(result.message || "An error occurred during login");
}
};
Expand Down
6 changes: 1 addition & 5 deletions components/signup-form.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use client";

import { useState } from "react";
import { useRouter } from "next/navigation";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm, Controller } from "react-hook-form";
import { Button } from "@/components/ui/button";
Expand All @@ -22,7 +21,6 @@ import Link from "next/link";
import FormAlert from "@/components/form-alert";

export default function SignupForm() {
const router = useRouter();
const [serverError, setServerError] = useState<string | null>(null);
const form = useForm<SignupInput>({
resolver: zodResolver(signupSchema),
Expand All @@ -38,9 +36,7 @@ export default function SignupForm() {
setServerError(null);
const result = await signup(data);

if (result.success) {
router.push("/dashboard");
} else {
if (!result.success) {
setServerError(result.message || "An error occurred while creating your account");
}
};
Expand Down
29 changes: 29 additions & 0 deletions lib/auth/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import "server-only";

type Config = {
jwt: JWTConfig;
};

type JWTConfig = {
defaultDuration: number;
refreshDuration: number;
secret: string;
issuer: string;
};

function envOrThrow(key: string) {
const value = process.env[key];
if (!value) {
throw new Error(`Environment variable ${key} is not set`);
}
return value;
}

export const config: Config = {
jwt: {
defaultDuration: 60 * 60, // 1 hour in seconds
refreshDuration: 60 * 60 * 24 * 60 * 1000, // 60 days in milliseconds
secret: envOrThrow("SESSION_SECRET"),
issuer: "spendi"
}
};
15 changes: 15 additions & 0 deletions lib/auth/password.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import argon2 from "argon2";

export async function hashPassword(password: string) {
return argon2.hash(password);
}

export async function checkPasswordHash(password: string, hash: string) {
if (!password) return false;

try {
return await argon2.verify(hash, password);
} catch {
return false;
}
}
Loading
Loading