Welcome to the Ever Works Directory Website Template, a cutting-edge, full-stack directory website solution built with Next.js 15.
This versatile template is an essential component of the Ever Works Platform, offering seamless integration while maintaining the flexibility to function as a standalone solution.
- Demo: https://demo.ever.works
- Ever Works website: https://ever.works (WIP)
- TypeScript
- NodeJs
- Framework: Next.js 15 with App Router
- Authentication: Auth.js / Supabase Auth
- API Client: Secure Axios-based client with httpOnly cookies
- ORM: Drizzle
- Supported Databases: Supabase/PostgreSQL/MySQL/SQLite
- Styling: Tailwind CSS
- UI Components: HeroUI React
- Internationalization: next-intl
- Form Validation: Zod
- Notifications/Emails Services: Novu / Resend
- Hosting: Vercel
- Payment Processing: Stripe & LemonSqueezy
- Security: ReCAPTCHA v2
├── .content/ # Content management directory
│ ├── posts/ # Blog posts
│ ├── categories/ # Category definitions
│ └── assets/ # Media files related to content
├── app/
│ ├── [locale]/ # Internationalized routes
│ ├── api/ # API routes
│ └── auth/ # Authentication pages
├── components/ # Reusable UI components
├── lib/ # Utility functions and config
├── public/ # Static files
└── styles/ # Global styles
The .content
folder acts as a Git-based CMS, synchronized with the repository specified in the DATA_REPOSITORY
environment variable.
- posts/: Markdown files for blog articles
- Each post has a frontmatter (title, date, author, etc.)
- Supports MDX for interactive content
- Organized by date and category
- categories/: Content organization
- YAML files for category configuration
- Supports nested categories
- Metadata and category relationships
- assets/: Media files related to content
- Images, documents, downloadable resources
- Organized according to content structure
Automatic sync via GitHub integration:
- Content is pulled from
DATA_REPOSITORY
- Changes are tracked via Git
- Updates occur periodically or on demand
- Requires a valid
GH_TOKEN
for private repos
Create a .env.local
file in the root directory with the following configuration:
# Environment
NODE_ENV=development
# API Configuration
NEXT_PUBLIC_API_BASE_URL="http://localhost:3000/api"
API_TIMEOUT=10000
API_RETRY_ATTEMPTS=3
API_RETRY_DELAY=1000
# Cookie Security
COOKIE_SECRET="your-secure-cookie-secret" # Generate with: openssl rand -base64 32
COOKIE_DOMAIN="localhost" # In production: your-domain.com
COOKIE_SECURE=false # In production: true
COOKIE_SAME_SITE="lax" # In production: strict
The .content/config.yml
file controls main site settings:
# Basic site settings
company_name: Acme # Company or site name
content_table: false # Enable/disable content table
item_name: Item # Singular name for items
items_name: Items # Plural name for items
copyright_year: 2025 # Footer copyright
# Auth settings
auth:
credentials: true # Email/password login
google: true # Google login
github: true # GitHub login
microsoft: true # Microsoft login
fb: true # Facebook login
x: true # X (Twitter) login
- Basic settings
company_name
: Your organization's namecontent_table
: Enable or disable content tableitem_name
/items_name
: Custom item labelscopyright
- Auth settings
- Enable/disable OAuth providers
- Use
true
to enable,false
to disable - Configure corresponding OAuth keys
💡 Note: Changes in config.yml are applied after syncing content or restarting the server.
- Node.js 18.x or higher
- PostgreSQL database (optional)
npm
oryarn
orpnpm
package manager
- Copy the
.env.example
file to.env.local
:
cp .env.example .env.local
- Fill in your environment variables in
.env.local
:
AUTH_SECRET="your-secret-key"
# Generate one with: openssl rand -base64 32
- Fork the repository:
- Visit https://github.com/ever-works/awesome-data
- Click "Fork" to create a copy
- This repo will hold
.content
data
- Configure GitHub integration:
GH_TOKEN='your-github-token'
DATA_REPOSITORY='https://github.com/ever-works/awesome-data'
💡 Important: The .content folder is created and synced automatically at startup with valid GitHub credentials.
DATABASE_URL=postgresql://user:password@localhost:5432/db_name
user
: PostgreSQL usernamepassword
: PostgreSQL passwordlocalhost
: Database host5432
: Default PostgreSQL portdb_name
: Name of your database
⚠️ Security: Never commit .env.local. Keep your secrets safe.
# Install dependencies
npm install
# or
yarn install
# Set up the database
npm run db:generate
npm run db:migrate
# Start the dev server
npm run dev
The app will be available at http://localhost:3000.
This template supports two payment providers: Stripe and LemonSqueezy. You can choose one or configure both.
The payment provider is configured in your site's config file (.content/config.yml
):
# Payment configuration
payment:
provider: 'stripe' # Options: 'stripe' | 'lemonsqueezy'
# Pricing plans
pricing:
free: 0
pro: 10
sponsor: 20
-
Create Stripe Account
- Visit Stripe Dashboard
- Create an account or sign in
- Get your API keys from the Developers section
-
Configure Environment Variables
# Stripe Configuration
STRIPE_SECRET_KEY="sk_test_your-stripe-secret-key"
STRIPE_PUBLISHABLE_KEY="pk_test_your-stripe-publishable-key"
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_your-stripe-publishable-key"
STRIPE_WEBHOOK_SECRET="whsec_your-webhook-secret"
# Stripe Price IDs (create these in Stripe Dashboard)
NEXT_PUBLIC_STRIPE_STANDARD_PRICE_ID="price_your-pro-price-id"
NEXT_PUBLIC_STRIPE_PREMIUM_PRICE_ID="price_your-sponsor-price-id"
-
Create Products & Prices in Stripe
- Go to Stripe Dashboard → Products
- Create products for each plan (Pro, Sponsor)
- Copy the Price IDs to your environment variables
-
Setup Webhooks
- Go to Stripe Dashboard → Webhooks
- Add endpoint:
https://yourdomain.com/api/webhooks/stripe
- Select events:
checkout.session.completed
,invoice.payment_succeeded
- Copy webhook secret to
STRIPE_WEBHOOK_SECRET
-
Create LemonSqueezy Account
- Visit LemonSqueezy
- Create an account and set up your store
-
Configure Environment Variables
# LemonSqueezy Configuration
LEMONSQUEEZY_API_KEY="your-lemonsqueezy-api-key"
LEMONSQUEEZY_STORE_ID="your-store-id"
LEMONSQUEEZY_WEBHOOK_SECRET="your-webhook-secret"
# LemonSqueezy Product IDs
NEXT_PUBLIC_LEMONSQUEEZY_PRO_PRODUCT_ID="your-pro-product-id"
NEXT_PUBLIC_LEMONSQUEEZY_SPONSOR_PRODUCT_ID="your-sponsor-product-id"
-
Create Products in LemonSqueezy
- Go to your LemonSqueezy store
- Create products for each plan
- Copy the Product IDs to your environment variables
-
Setup Webhooks
- Go to Settings → Webhooks
- Add webhook URL:
https://yourdomain.com/api/webhooks/lemonsqueezy
- Copy webhook secret to environment variables
To switch between payment providers:
- Update config.yml
payment:
provider: 'lemonsqueezy' # Change from 'stripe' to 'lemonsqueezy'
-
Restart your application for changes to take effect
-
Ensure environment variables are configured for your chosen provider
- ✅ Subscription Management: Create, update, cancel subscriptions
- ✅ Webhook Handling: Automatic payment status updates
- ✅ Customer Portal: Self-service billing management
- ✅ Multiple Plans: Free, Pro, and Sponsor tiers
- ✅ Secure Processing: PCI-compliant payment handling
- ✅ International Support: Multiple currencies and payment methods
-
Cookie Security
- httpOnly cookies are used for token storage
- Prevents XSS attacks by making tokens inaccessible to JavaScript
- Secure flag must be enabled in production
- SameSite policy helps prevent CSRF attacks
-
API Security
- Automatic token refresh handling
- Request queue during token refresh
- Exponential backoff for retries
- Proper error handling and formatting
-
Environment Specific
- Development uses relaxed security for local testing
- Production requires strict security settings
- Different cookie domains per environment
- CORS configuration required for production
This template includes Google ReCAPTCHA v2 for form protection against spam and bots.
-
Get ReCAPTCHA Keys
- Visit Google ReCAPTCHA Console
- Create a new site with reCAPTCHA v2 ("I'm not a robot" checkbox)
- Add your domains (localhost for development, your domain for production)
-
Configure Environment Variables
# ReCAPTCHA Configuration
NEXT_PUBLIC_RECAPTCHA_SITE_KEY="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI" # Site key (public)
RECAPTCHA_SECRET_KEY="6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" # Secret key (private)
💡 Development: The keys above are Google's test keys that always pass validation
- ✅ Form Protection: Login, registration, and contact forms
- ✅ Server-side Verification: Secure token validation
- ✅ React Query Integration: Optimized API calls with caching
- ✅ Error Handling: User-friendly error messages
- ✅ Responsive Design: Works on all device sizes
- ✅ Accessibility: Screen reader compatible
ReCAPTCHA can be enabled/disabled per form by modifying the component props:
// Enable ReCAPTCHA
<LoginForm showRecaptcha={true} />
// Disable ReCAPTCHA
<LoginForm showRecaptcha={false} />
The template includes a robust API client (lib/api/server-client.ts
) with advanced features:
- ✅ Automatic Retries: 3 attempts with exponential backoff
- ✅ Timeout Handling: Configurable request timeouts
- ✅ Error Management: Centralized error handling and logging
- ✅ TypeScript Support: Fully typed API responses
- ✅ Request/Response Interceptors: Middleware support
import { serverClient, apiUtils } from '@/lib/api/server-client';
// GET request
const users = await serverClient.get<User[]>('/api/users');
if (apiUtils.isSuccess(users)) {
console.log(users.data); // TypeScript knows this is User[]
}
// POST request with data
const result = await serverClient.post('/api/auth/login', {
email: 'user@example.com',
password: 'password123'
});
// File upload
const file = new File(['content'], 'document.pdf');
const upload = await serverClient.upload('/api/upload', file);
// Form data submission
const contact = await serverClient.postForm('/api/contact', {
name: 'John Doe',
email: 'john@example.com',
message: 'Hello world'
});
// External API client (longer timeout, fewer retries)
import { externalClient } from '@/lib/api/server-client';
const external = await externalClient.get('https://api.external.com/data');
// ReCAPTCHA client
import { recaptchaClient } from '@/lib/api/server-client';
const verification = await recaptchaClient.verify(token);
// Custom client
import { createApiClient } from '@/lib/api/server-client';
const customClient = createApiClient('https://api.myservice.com', {
timeout: 30000,
retries: 5,
headers: { 'Authorization': 'Bearer token' }
});
const client = new ServerClient('https://api.example.com', {
timeout: 10000, // Request timeout (ms)
retries: 3, // Number of retry attempts
retryDelay: 1000, // Delay between retries (ms)
headers: { // Default headers
'Authorization': 'Bearer token',
'X-API-Version': 'v2'
}
});
import { api } from 'lib/api/api-client';
// Authentication
await api.login({ email: 'user@example.com', password: 'password' });
// Check authentication status
if (await api.isAuthenticated()) {
// Make authenticated requests
const response = await api.get('/protected-endpoint');
}
// Logout
await api.logout();
- Database Studio:
npm run db:studio
- Linting:
npm run lint
- Type Checking:
tsc
or during build
- Pages: Add in
app/[locale]
- API: Create endpoints in
app/api
- Components: Add to
components
- Database: Edit schema in
lib/db/schema.ts
- Add translations under
messages
- Use
useTranslations
in components - Add new locales in config
- Configure providers in
auth.config.ts
- Protect routes via middleware
- Customize auth pages in
app/[locale]/auth
# Auth Endpoints
AUTH_ENDPOINT_LOGIN="/auth/login"
AUTH_ENDPOINT_REFRESH="/auth/refresh"
AUTH_ENDPOINT_LOGOUT="/auth/logout"
AUTH_ENDPOINT_CHECK="/auth/check"
# JWT Configuration
JWT_ACCESS_TOKEN_EXPIRES_IN=15m
JWT_REFRESH_TOKEN_EXPIRES_IN=7d
# CORS Settings
CORS_ORIGIN="https://your-frontend-domain.com"
CORS_CREDENTIALS=true
CORS_METHODS="GET,POST,PUT,DELETE,OPTIONS"
The easiest way to deploy the app is via the Vercel platform.
Check the Next.js deployment docs.
AGPL v3
Ever® is a registered trademark of Ever Co. LTD. Ever® Works™, Ever® Demand™, Ever® Gauzy™, Ever® Teams™ and Ever® OpenSaaS™ are all trademarks of Ever Co. LTD.
The trademarks may only be used with the written permission of Ever Co. LTD. and may not be used to promote or otherwise market competitive products or services.
All other brand and product names are trademarks, registered trademarks, or service marks of their respective holders.
- Please give us a ⭐ on Github, it helps!
- You are more than welcome to submit feature requests in the separate repo
- Pull requests are always welcome! Please base pull requests against the develop branch and follow the contributing guide.
See our contributors list in CONTRIBUTORS.md. You can also view a full list of our contributors tracked by Github.