A modern agricultural input management system built with Next.js, Supabase, and custom JWT authentication.
- Framework: Next.js 16 (App Router)
- Database: Supabase (PostgreSQL)
- Authentication: Custom JWT (using
joseandbcryptjs) - Styling: Tailwind CSS
- State Management: React Query + React Context
- Language: TypeScript
- 🌾 Product catalogue with search and filtering
- 🛒 Order management system
- 👤 User authentication (signup, login, logout)
- 🔐 Role-based access control (Admin/User)
- 📊 Admin dashboard for managing products, orders, and users
- Node.js 18+
- pnpm (recommended) or npm
- A Supabase project
git clone <your-repo-url>
cd efarmpnpm installCreate a .env.local file in the root directory:
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key
JWT_SECRET=your_random_secret_at_least_32_characters_longImportant: Generate a secure
JWT_SECRETusing:[Convert]::ToBase64String((1..32 | % { [byte](Get-Random -Min 0 -Max 256) }))
Run the following SQL in your Supabase SQL Editor to create the required tables:
-- Enable UUID generation
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
-- Profiles table (stores users)
CREATE TABLE profiles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
full_name TEXT,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
role TEXT NOT NULL CHECK (role IN ('admin', 'user')) DEFAULT 'user',
created_at TIMESTAMP DEFAULT now()
);
-- Products table
CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
category TEXT NOT NULL,
description TEXT,
price NUMERIC NOT NULL,
stock_quantity INT NOT NULL,
image_url TEXT,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT now()
);
-- Orders table
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
product_id UUID REFERENCES products(id),
user_id UUID REFERENCES profiles(id),
quantity INT NOT NULL,
total_price NUMERIC NOT NULL,
status TEXT DEFAULT 'pending',
created_at TIMESTAMP DEFAULT now()
);pnpm devOpen http://localhost:3000 in your browser.
This project uses a custom JWT authentication system instead of Supabase Auth. This approach bypasses email verification requirements and gives full control over the auth flow.
- Sign Up: User credentials are validated, password is hashed with
bcryptjs, and stored in theprofilestable. - Login: Password is verified against the stored hash. A JWT session token is created using
joseand stored in an HTTP-only cookie. - Session Management: The middleware reads the session cookie on every request to protect routes and handle role-based redirects.
- Logout: The session cookie is cleared.
| File | Purpose |
|---|---|
lib/auth.ts |
Core auth utilities (hash, compare, encrypt, decrypt, login, logout, getSession, getUser) |
app/auth/actions.ts |
Server Actions for login, register, logout |
middleware.ts |
Route protection and role-based redirects |
providers/AuthProvider.tsx |
Client-side auth state management |
app/api/auth/me/route.ts |
API endpoint to fetch current user session |
By default, all new users are created with the user role. To promote a user to admin:
- Open your Supabase Dashboard → SQL Editor
- Run:
UPDATE profiles
SET role = 'admin'
WHERE email = 'user@example.com';- Log out and log back in to refresh the session with the new role.
efarm/
├── app/
│ ├── (main)/ # User-facing pages (home, products, orders)
│ ├── admin/ # Admin dashboard pages
│ ├── api/ # API routes
│ ├── auth/ # Auth server actions
│ ├── login/ # Login page
│ └── register/ # Registration page
├── components/ # Reusable UI components
├── lib/
│ ├── auth.ts # JWT authentication utilities
│ └── supabase/ # Supabase client configuration
├── providers/ # React context providers
├── types/ # TypeScript type definitions
└── supabase/
└── schema.sql # Database schema
| Command | Description |
|---|---|
pnpm dev |
Start development server |
pnpm build |
Build for production |
pnpm start |
Start production server |
pnpm lint |
Run ESLint |
The role is stored in the JWT session cookie. You must log out and log back in to get a new token with the updated role.
Ensure secure: false is set for cookies in development (already configured in lib/auth.ts).
The project uses /login and /register routes. If you see 404s, check that middleware redirects point to the correct paths.
MIT