Polymer is the rebuilt web platform for The Polytechnic, RPI's independent student newspaper. This repository combines the public site, editorial CMS, archive-aware search, and deployment tooling in a single Next.js + Payload application.
- Serves the public homepage and section pages for
news,features,sports, andopinion - Renders article pages with standard, opinion, and photofeature layouts
- Hosts staff pages and public author profiles
- Provides a Payload admin panel for publishing, media management, and homepage curation
- Searches both the current Payload content store and the legacy Poly archive
- Deploys to a self-hosted production environment with smoke checks and rollback support
- Next.js 16 with the App Router
- React 19
- TypeScript
- Payload CMS 3
- PostgreSQL
- Tailwind CSS 4
- Playwright
- pnpm
.
|-- app/
| |-- (frontend)/ # Public-facing routes
| |-- (payload)/ # Payload admin + API routes
| `-- api/ # Search, health, weather, and utility endpoints
|-- collections/ # Payload collection definitions
|-- components/ # Shared UI, article layouts, dashboard UI
|-- migrations/ # SQL / Payload migration assets
|-- public/ # Static assets, icons, and fonts
|-- scripts/ # Deploy and local bootstrap scripts
|-- tests/ # Playwright smoke and search tests
|-- utils/ # Formatting, routing, rate limiting, search helpers
|-- payload.config.ts # Payload config and Postgres adapter setup
|-- next.config.ts # Next.js config wrapped with Payload
`-- .github/workflows/ # CI and production deployment workflows
articles: Draft/published stories with section, authors, featured media, subdeck, and rich text contentlayout: The curated homepage configuration, including lead story and pinned slotsusers: Staff accounts, roles, headshots, bios, and position historymedia: Uploaded assets with alt text and photographer attributionjob-titles: Reusable staff titles for profile timelines
Article write access is role-based. Admins, EICs, and section editors can update articles; writers can create them; anonymous visitors only see published content.
- Node.js 20+
- pnpm
- PostgreSQL
pnpm installIf .env does not exist, the postinstall script will generate one and create a random PAYLOAD_SECRET.
DATABASE_URL=postgres://USER:PASSWORD@HOST:5432/polymer
PAYLOAD_SECRET=replace-with-a-long-random-stringLEGACY_DATABASE_URI=postgres://USER:PASSWORD@HOST:5432/legacy_poly
NEXT_PUBLIC_SITE_URL=http://localhost:3000
BASE_URL=http://127.0.0.1:3000
PLAYWRIGHT_WEB_SERVER=1
PLAYWRIGHT_WEB_SERVER_COMMAND=pnpm devLEGACY_DATABASE_URI is required if you want archive-backed search results from the old site. Without it, the current-content site can still run, but the legacy search endpoints will not be fully functional.
pnpm devOpen http://localhost:3000 for the public site and http://localhost:3000/admin for the CMS. On a fresh database, Payload will prompt you to create the first admin user.
pnpm dev: Start the local Next.js + Payload dev serverpnpm build: Build the production apppnpm start: Run the built apppnpm lint: Run ESLintpnpm typecheck: Run TypeScript without emitting filespnpm test:smoke: Run the main Playwright smoke suitepnpm test:search-limit: Run the search query bounds testpnpm test:deploy-smoke: Run the post-deploy health/smoke scriptpnpm generate:types: Regeneratepayload-types.ts
- Homepage content is driven by the
layoutcollection rather than a hardcoded editorial order. - Search combines Payload content with legacy Wagtail-era content through
LEGACY_DATABASE_URI. - The health endpoint is
GET /api/healthand verifies that the app can reach the primary database. - The article route format is
/:section/:year/:month/:slug.
The Playwright suite checks that key public routes render without framework errors, uncaught page errors, or broken critical assets. The smoke coverage includes:
- Homepage
- Section pages
- Article pages
- Staff pages
- Search input sanitization behavior
For local smoke runs:
pnpm test:smoke
pnpm test:search-limitProduction deploys are triggered by GitHub Actions after the CI workflow succeeds on a push to main.
The deploy workflow:
- Creates a fresh release directory on the self-hosted server
- Installs dependencies with
pnpm install --frozen-lockfile - Links shared runtime assets like
.envandmedia - Runs SQL migrations before the app build
- Builds the app
- Atomically switches the active release
- Reloads the
polymerPM2 process - Verifies
/api/healthand runs deploy smoke checks - Rolls back to the previous release automatically if verification fails
Because main is deployment-sensitive, treat it as a release branch rather than a scratch branch.
MIT. See LICENSE.