A comprehensive digital platform for identifying, understanding, and managing agricultural pests and weeds.
Built for agronomists, staff, and administrators with role-based access control and a growing guide library.
Share Project Repository
Tech Stack:
Important
This project is a modern full-stack biosecurity information system built with Next.js 16, React 19, Better Auth, Drizzle ORM, and Neon PostgreSQL. It is deployed on Cloudflare Workers via the @opennextjs/cloudflare adapter for global edge performance. It provides role-based dashboards for administrators, staff, and agronomists to manage and browse pest and weed identification guides with detailed scientific data, images, and control methods.
Table of Contents
- Introduction
- Key Features
- Architecture
- Tech Stack
- Getting Started
- Deployment
- Project Structure
- Contributing
- License
- Author
|
The Biosecurity Guide is a comprehensive digital platform designed to support agricultural professionals in identifying, understanding, and managing pests and weeds. The system provides a centralized guide library with detailed information on pest and weed characteristics, biology, impacts, and control methods — all backed by visual references. The platform supports three distinct user roles — Administrator, Staff, and Agronomist — each with tailored dashboards and capabilities, ensuring the right level of access for every user. Agricultural biosecurity is critical for protecting crops, ecosystems, and food supply chains. Agronomists and field workers need quick, reliable access to pest and weed identification data to make timely decisions. This platform consolidates that information into a searchable, managed, and role-protected system — replacing scattered documents and outdated references with a modern, responsive web application.
|
Note
- Node.js >= 18.0 required
- Neon PostgreSQL account required for database
- Cloudflare account required for deployment
Detailed guides on agricultural pests including common and scientific names, key characteristics, biology, impacts, and control methods — all with visual image references to aid field identification.
Key capabilities:
- Comprehensive pest profiles with scientific data
- Multiple image support per guide entry
- Structured control method recommendations
- Impact assessment information
Comprehensive weed identification and control guides to protect agricultural productivity and biodiversity. Each entry includes lifecycle data, distinguishing characteristics, and recommended management strategies.
Three distinct user roles with tailored dashboards and permissions:
| Role | Capabilities |
|---|---|
| Administrator | Full platform access — manage guides, staff, agronomists, and all system settings |
| Staff | Create, edit, and delete guides; view agronomist profiles; manage own profile |
| Agronomist | Browse and search the guide library; manage own profile |
Browse and search through the guide library with powerful filtering capabilities:
- Field-specific filtering (common name, scientific name, characteristics, biology, impacts, control)
- Case-insensitive search matching
- Pagination for large datasets
- Filter by type (pest or weed)
- Secure authentication with server-side sessions (Better Auth)
- Image carousel for visual guide references
- Responsive design for desktop and mobile use
- Light/dark theme support
- Profile management for all user roles
- Form validation with Zod schemas
- Modern UI with shadcn/ui components
graph TB
subgraph Client["Client (Browser)"]
UI[React 19 UI<br/>shadcn/ui + Tailwind CSS 4]
AuthClient[Better Auth Client]
end
subgraph CloudflareEdge["Cloudflare Workers (Edge)"]
Worker[OpenNext Worker]
Middleware[Next.js Middleware<br/>Session Cookie Check]
SSR[Next.js SSR<br/>App Router]
API[API Routes<br/>REST Endpoints]
Assets[Static Assets<br/>Cloudflare CDN]
end
subgraph ExternalServices["External Services"]
NeonDB[(Neon PostgreSQL<br/>Serverless HTTP)]
R2[Cloudflare R2<br/>Incremental Cache]
Pixabay[Pixabay CDN<br/>Guide Images]
end
UI -->|HTTPS| Worker
AuthClient -->|Auth API| API
Worker --> Middleware
Middleware -->|Protected Routes| SSR
Middleware -->|Public Routes| SSR
Worker -->|Static Files| Assets
SSR --> API
API -->|Drizzle ORM| NeonDB
API -->|Better Auth| NeonDB
Worker -->|Cache| R2
UI -->|Images| Pixabay
sequenceDiagram
actor User
participant CF as Cloudflare Worker
participant MW as Middleware
participant Page as Next.js Page
participant API as API Route
participant Auth as Better Auth
participant DB as Neon PostgreSQL
User->>CF: HTTPS Request
CF->>MW: Route Request
alt Static Asset
CF-->>User: Serve from CDN Cache
else Public Route (/, /login, /sources)
MW->>Page: Allow Access
Page-->>User: Render Page
else Protected Route (/admin/*, /staff/*, etc.)
MW->>MW: Check Session Cookie
alt No Session Cookie
MW-->>User: Redirect to /login
else Has Session Cookie
MW->>Page: Allow Access
Page->>API: Fetch Data
API->>Auth: Verify Session
Auth->>DB: Query Session
DB-->>Auth: Session Data
API->>DB: Query Data
DB-->>API: Results
API-->>Page: JSON Response
Page-->>User: Render Page
end
end
erDiagram
user {
text id PK
text name
text email UK
text username UK
text role "administrator | staff | agronomist"
text status "active | inactive"
boolean emailVerified
text image
timestamp createdAt
timestamp updatedAt
}
session {
text id PK
text userId FK
text token UK
text ipAddress
text userAgent
timestamp expiresAt
timestamp createdAt
timestamp updatedAt
}
account {
text id PK
text userId FK
text accountId
text providerId
}
verification {
text id PK
text identifier
text value
timestamp expiresAt
timestamp createdAt
timestamp updatedAt
}
agronomists {
serial id PK
text userId FK
text firstName
text lastName
text phoneNumber
text address
}
staff_and_administrators {
serial id PK
text userId FK
text firstName
text lastName
text position
text department
}
agriculture_items {
serial id PK
enum itemType "pest | weed"
text commonName
text scientificName
text keyCharacteristics
text biology
text impacts
text control
text source
}
images {
serial id PK
integer itemId FK
text url
text altText
boolean isPrimary
}
user ||--o{ session : "has"
user ||--o{ account : "has"
user ||--o| agronomists : "profile"
user ||--o| staff_and_administrators : "profile"
agriculture_items ||--o{ images : "has"
Frontend:
- Framework: Next.js 16 with App Router
- Language: TypeScript for type safety
- Styling: Tailwind CSS 4 + tw-animate-css
- UI Components: shadcn/ui (Base UI React)
- Icons: Lucide React
- Themes: next-themes for light/dark mode
- Notifications: Sonner toast system
Backend:
- Runtime: Next.js API Routes on Cloudflare Workers (edge)
- Authentication: Better Auth with email/password and server-side sessions
- Database ORM: Drizzle ORM with type-safe queries
- Validation: Zod schema validation
Database:
- Provider: Neon serverless PostgreSQL (HTTP driver, edge-compatible)
- Migrations: Drizzle Kit
- Schema: Custom tables for users, guides, images, profiles with role-based enums
Infrastructure:
- Hosting: Cloudflare Workers via
@opennextjs/cloudflareadapter - Static Assets: Cloudflare CDN (automatic edge caching)
- Incremental Cache: Cloudflare R2 bucket
- Build: Webpack bundler (required for Cloudflare compatibility)
Important
Ensure you have the following installed:
- Node.js 18.0+ (Download)
- npm package manager
- Git (Download)
- A Neon PostgreSQL database account
- A Cloudflare account (for deployment)
1. Clone Repository
git clone https://github.com/ChanMeng666/biosecurity.git
cd biosecurity2. Install Dependencies
npm install3. Configure Environment Variables
# Copy environment template
cp .env.example .env.local
# Edit environment variables
nano .env.localCreate .env.local with the following variables:
# Neon Database (get from neonctl connection-string)
DATABASE_URL=postgresql://user:password@ep-xxx.us-east-2.aws.neon.tech/neondb?sslmode=require
# Better Auth (generate a random 32+ character secret)
BETTER_AUTH_SECRET=your-random-secret-here
BETTER_AUTH_URL=http://localhost:3000
# App URL
NEXT_PUBLIC_APP_URL=http://localhost:3000Quick Reference:
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL |
Yes | Neon PostgreSQL connection string |
BETTER_AUTH_SECRET |
Yes | Auth encryption key (32+ chars) |
BETTER_AUTH_URL |
Yes | Auth base URL |
NEXT_PUBLIC_APP_URL |
Yes | Public-facing app URL (inlined at build time) |
Tip
Use openssl rand -base64 32 to generate a secure random secret for BETTER_AUTH_SECRET.
4. Run Database Migrations
# Generate migration files from schema
npm run db:generate
# Push schema to database
npm run db:pushTip
Use npm run db:studio to open the Drizzle Studio GUI for browsing your database.
5. Start Development Server
npm run devOpen http://localhost:3000 to view the application.
Available Scripts:
npm run dev # Start dev server with hot reload
npm run build # Production build (webpack)
npm run start # Start production server (local Node.js)
npm run build:worker # Build for Cloudflare Workers
npm run preview # Preview Cloudflare build locally
npm run cf:deploy # Build and deploy to Cloudflare Workers
npm run lint # Run ESLint
npm run db:generate # Generate Drizzle migration files
npm run db:push # Push schema changes to database
npm run db:migrate # Run pending migrations
npm run db:studio # Open Drizzle Studio database GUIThis project is deployed on Cloudflare Workers using the @opennextjs/cloudflare adapter.
Live URL: https://biosecurity.chanmeng-dev.workers.dev
graph LR
subgraph Build["Build Pipeline"]
Next["next build --webpack"]
OpenNext["opennextjs-cloudflare build"]
Next --> OpenNext
end
subgraph Output["Build Output (.open-next/)"]
WorkerJS["worker.js<br/>Entry Point"]
ServerFn["server-functions/<br/>SSR + API"]
StaticAssets["assets/<br/>CSS, JS, Fonts"]
end
subgraph Cloudflare["Cloudflare Edge Network"]
CDN["CDN<br/>Static Assets"]
Workers["Workers Runtime<br/>nodejs_compat"]
R2["R2 Bucket<br/>Incremental Cache"]
end
subgraph External["External"]
Neon["Neon PostgreSQL<br/>HTTP Driver"]
end
OpenNext --> WorkerJS
OpenNext --> ServerFn
OpenNext --> StaticAssets
StaticAssets -->|wrangler deploy| CDN
WorkerJS -->|wrangler deploy| Workers
ServerFn -->|bundled into| Workers
Workers -->|Cache Read/Write| R2
Workers -->|SQL over HTTP| Neon
# 1. Set Cloudflare secrets (first time only)
wrangler secret put DATABASE_URL
wrangler secret put BETTER_AUTH_SECRET
# 2. Build and deploy
NEXT_PUBLIC_APP_URL=https://biosecurity.chanmeng-dev.workers.dev npm run cf:deployNote
NEXT_PUBLIC_APP_URLmust be set at build time (it is inlined into client JavaScript bundles).- The build uses
--webpackinstead of Turbopack for Cloudflare compatibility. - The
nodejs_compatcompatibility flag is enabled inwrangler.jsoncto support Node.js APIs used by Better Auth.
| File | Purpose |
|---|---|
wrangler.jsonc |
Cloudflare Worker configuration (bindings, compatibility flags, env vars) |
open-next.config.ts |
OpenNext adapter configuration |
next.config.ts |
Next.js config (standalone output, custom image loader) |
src/lib/image-loader.ts |
Custom image loader for Cloudflare (pass-through for external URLs) |
biosecurity/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── (auth)/ # Authentication routes
│ │ │ ├── login/ # Login page
│ │ │ └── register/ # Registration pages
│ │ │ ├── admin/ # Admin registration
│ │ │ ├── agronomist/ # Agronomist registration
│ │ │ └── staff/ # Staff registration
│ │ ├── (dashboard)/ # Protected dashboard routes
│ │ │ ├── admin/ # Admin dashboard
│ │ │ │ ├── manage-guides/ # Guide CRUD management
│ │ │ │ ├── manage-agronomists/ # Agronomist management
│ │ │ │ └── manage-staff/ # Staff management
│ │ │ ├── agronomist/ # Agronomist dashboard
│ │ │ │ └── guides/ # Guide browsing
│ │ │ └── staff/ # Staff dashboard
│ │ │ ├── manage-guides/ # Guide CRUD management
│ │ │ └── view-agronomists/ # View agronomist profiles
│ │ ├── api/ # API routes
│ │ │ ├── auth/[...all]/ # Better Auth handler
│ │ │ ├── guides/ # Guide CRUD endpoints
│ │ │ ├── agronomists/ # Agronomist endpoints
│ │ │ ├── staff/ # Staff endpoints
│ │ │ └── profile/ # Profile endpoints
│ │ └── sources/ # Sources & credits page
│ ├── components/
│ │ ├── ui/ # shadcn/ui base components
│ │ ├── guides/ # Guide-specific components
│ │ ├── layout/ # Navbar, dashboard shell
│ │ ├── shared/ # Confirm dialog, data table
│ │ └── users/ # Profile editor
│ └── lib/
│ ├── auth/ # Better Auth config & helpers
│ ├── db/ # Drizzle ORM, schema, migrations
│ ├── image-loader.ts # Custom Cloudflare image loader
│ └── validators/ # Zod validation schemas
├── wrangler.jsonc # Cloudflare Worker configuration
├── open-next.config.ts # OpenNext adapter configuration
├── next.config.ts # Next.js configuration
├── drizzle.config.ts # Drizzle ORM configuration
├── package.json # Dependencies and scripts
└── tsconfig.json # TypeScript configuration
Contributions are welcome! Here's how you can help:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Guidelines:
- Follow TypeScript best practices
- Use the existing code style and ESLint configuration
- Test your changes locally before submitting
- Write clear commit messages
This project is licensed under the MIT License - see the LICENSE file for details.
Chan Meng
A comprehensive biosecurity guide for agricultural pest and weed management