A modern, full-stack web platform for the Guild of Physics, built with Next.js, Payload CMS, and PostgreSQL.
This project is designed for extensibility, internationalization, and rich content management.
- Node.js: v18.20.2 or >=20.9.0
- pnpm: v10+
- Docker (optional, for containerized development/production)
- PostgreSQL: (if not using Docker Compose's built-in service)
git clone <repo-url>
cd fk-web
pnpm install
Create a .env
file in the root directory.
Required variables (see codebase for more, depending on features used):
# Server and site
NEXT_NEXT_PUBLIC_SERVER_URL=http://localhost:3000
# Database
DATABASE_URI=postgres://user:password@localhost:5432/dbname
# Payload CMS
PAYLOAD_SECRET=your-secret
# Email (SMTP)
EMAIL_FROM_NAME=Fyysikkokilta
EMAIL_FROM_ADDRESS=your@email.com
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your-smtp-user
SMTP_PASSWORD=your-smtp-password
# Google APIs
GOOGLE_API_KEY=your-google-api-key
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_SITE_VERIFICATION=your-google-site-verification
# OAuth
ALLOW_NON_EXISTING_USERS=false
# S3
S3_BUCKET=...
S3_ACCESS_KEY_ID=...
S3_SECRET=...
S3_ENDPOINT=...
NEXT_PUBLIC_S3_PUBLIC_URL=...
# Form builder
FORM_BUILDER_DEFAULT_TO_EMAIL=...
# Job Queue
JOB_QUEUE_SECRET=your-job-queue-secret
# Analyze bundle (optional)
ANALYZE=true
- By default, the project expects a PostgreSQL database.
- You can use Docker Compose to spin up the app and (optionally) a database.
Uncomment the postgres
service in docker-compose.yml
if you want to use the built-in database.
docker-compose up --build
- Create a PostgreSQL database and user.
- Set
DATABASE_URI
accordingly in your.env
.
pnpm dev
pnpm build
pnpm start
Or use Docker:
docker build -t fk-web .
docker run -p 3000:3000 --env-file .env fk-web
src/
app/ # Next.js app directory (frontend routes, layouts, API)
(frontend)/ # Main frontend, locale-based routing
(payload)/ # Payload CMS admin interface
blocks/ # Reusable content blocks (Calendar, TwoColumns, etc.)
collections/ # Payload CMS collections (Pages, Newsletters, Media, etc.)
components/ # Shared React components (RichText, Navigation, etc.)
emails/ # Email templates
fields/ # Custom Payload field configs
globals/ # Global site settings (Footer, Navigation, etc.)
hooks/ # Custom hooks for Payload/Next.js
lib/ # Utility libraries (calendar, etc.)
utils/ # Utility functions
payload.config.ts # Payload CMS configuration
payload-types.ts # Auto-generated Payload types
- Next.js (App Router, SSR, i18n)
- Payload CMS (headless CMS, admin UI, API)
- PostgreSQL (database)
- Tailwind CSS (utility-first styling)
- date-fns (date utilities)
- lucide-react (icon set)
- next-intl (internationalization)
- S3 (Cloudflare) (cloud storage, optional)
- Plaiceholder (image placeholders)
- React Hook Form (forms)
- Nodemailer (email via Payload plugin)
- Payload Plugins: SEO, Redirects, Form Builder, Import/Export, Storage, etc.
- Calendar: Google Calendar integration, color-coded events
- TwoColumns: Responsive two-column layout
- Align: Horizontally align and set width for rich text content
- Newsletter: Newsletter content and settings
- PDFViewer: Embed PDFs
- BoardMemberGrid: Display board members
- Collapsible: Expand/collapse content
- EmbedVideo: YouTube/Vimeo embeds
- ...and more
- Uses pnpm for package management.
- Uses ESLint and Prettier for code style.
- Uses Docker for production and local development.
- All media and document uploads are stored in
/public/media
and/public/documents
(volumes in Docker). - Rich text editing is powered by Payload's Lexical editor.
The project uses Payload CMS's built-in job queue system for handling background tasks like newsletter sending and scheduled publishing.
Jobs are configured in src/payload.config.ts
and can be triggered via API endpoints.
Jobs can be executed using the /api/payload-jobs/run
endpoint:
# Trigger a job via API
curl -X POST http://localhost:3000/api/payload-jobs/run \
-H "Content-Type: application/json" \
-H "x-job-queue-secret: your-job-queue-secret" \
-d '{"task": "sendNewsletter", "data": {"newsletterId": 123}}'
- schedulePublish: Handles scheduled content publishing
- sendNewsletter: Sends newsletters to subscribers
JOB_QUEUE_SECRET
: Secret key for API authentication (required for job execution)
For more information, see the Payload CMS Jobs Queue documentation.
pnpm dev
– Start development serverpnpm build
– Build for productionpnpm start
– Start production serverpnpm lint
– Lint codepnpm generate:types
– Generate Payload typespnpm generate:importmap
– Generate import map for Payload
MIT