Production website and small admin CMS for Regent Technologies, an industrial blade sharpening and tooling support company in Moratuwa, Sri Lanka.
The public site covers services, products, industries, FAQ, contact, legal pages, and an About page. The private admin area lives only under /hidden-admin and manages products, FAQ, and the admin profile. Public pages can render without a database by using seeded fallback content; write actions and admin pages require production services.
- Next.js App Router with Server Components and Metadata API
- Better Auth for admin email/password authentication
- Drizzle ORM with Neon Postgres
- Cloudflare R2 presigned uploads for product images
- Resend for password reset and contact emails
- Cloudflare Turnstile for contact form protection
- Dynamic sitemap, robots rules, canonical metadata, Open Graph/Twitter metadata, and JSON-LD structured data
npm install
npm run devOpen http://localhost:3000.
Copy .env.example to .env.local and fill only the services you are testing locally. Public pages can build without a database, but admin actions require DATABASE_URL.
For local admin testing, point DATABASE_URL to a local Postgres database, run migrations, then seed the admin account. For production, replace the same DATABASE_URL value with Neon.
Core production:
NEXT_PUBLIC_SITE_URL="https://www.regenttech.com"
DATABASE_URL="postgresql://..."
BETTER_AUTH_URL="https://www.regenttech.com"
BETTER_AUTH_SECRET="generate-a-32-byte-or-longer-secret"Local Postgres example:
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/regent"
BETTER_AUTH_URL="http://localhost:3000"
BETTER_AUTH_SECRET="use-a-local-32-byte-or-longer-secret"Email:
RESEND_API_KEY="..."
RESEND_FROM_EMAIL="Regent Technologies <noreply@your-domain.com>"Turnstile:
NEXT_PUBLIC_TURNSTILE_SITE_KEY="..."
TURNSTILE_SECRET_KEY="..."Use both keys from the same Cloudflare Turnstile widget and add the production domain in Cloudflare. For local development, leave both Turnstile values empty to bypass the challenge. Do not set only TURNSTILE_SECRET_KEY; the server will reject submissions because the browser cannot produce a token without NEXT_PUBLIC_TURNSTILE_SITE_KEY.
Cloudflare R2:
R2_ACCOUNT_ID="..."
R2_ACCESS_KEY_ID="..."
R2_SECRET_ACCESS_KEY="..."
R2_BUCKET="..."
R2_PUBLIC_BASE_URL="https://cdn.example.com"First admin seed only:
ADMIN_EMAIL="..."
ADMIN_INITIAL_PASSWORD="..."Do not commit real admin credentials or filled env files.
- Page titles use the pattern
Page Title - Regent Technologies. - Public pages have canonical URLs, descriptions, Open Graph, and Twitter card metadata.
- Search and paginated product result URLs are noindexed while canonicalizing to
/products. - Product detail pages include Product JSON-LD; the FAQ page includes FAQPage JSON-LD; the root layout includes LocalBusiness and WebSite JSON-LD.
/sitemap.xmlincludes public static pages, industry detail pages, and published product detail pages./robots.txtallows public content and blocks/hidden-admin/and/api/.- Legal pages are noindexed because they are required trust content, not search landing pages.
Generate a migration after schema changes:
npm run db:generateApply migrations:
npm run db:migrateSeed the initial admin, products, and FAQ:
npm run db:seedThe seed script is idempotent and reads the admin email/password from env. Sign-up is disabled during normal runtime.
- Login:
/hidden-admin - Dashboard:
/hidden-admin/dashboard - Products:
/hidden-admin/dashboard/products - FAQ:
/hidden-admin/dashboard/faqs - Profile:
/hidden-admin/dashboard/profile
Product fields:
nameanddescriptionare required.slug,metaTitle, andmetaDescriptionauto-fill when left blank.- Up to 3 images are supported.
- R2 uploads are available when R2 env vars are configured.
- Add all production env vars in Vercel.
- Run
npm run db:migrateagainst Neon. - Run
npm run db:seedonce with admin seed env vars present. - Remove seed-only env vars from Vercel after the admin account exists.
- Deploy and verify
/,/products,/industries,/contact,/faq, and/hidden-admin. - Submit both the contact form and a product inquiry once with production Turnstile keys enabled.
Before release, check these routes on desktop and mobile widths:
//about/services/products/products/precision-blade-sharpening/industries/industries/woodworking-industry/contact/faq/hidden-admin/hidden-admin/dashboard
npm run lint
npm run typecheck
npm run build
npm audit --omit=devDo not run npm audit fix --force without reviewing the resulting dependency graph. This project uses narrow package overrides for vulnerable transitive packages when the direct dependencies are already on their latest compatible versions.