Skip to content

Repository files navigation

Commons by Codezela

Commons by Codezela

An open-source platform for publishing and discovering research papers, technical articles, and peer-reviewed content.

Built by Codezela Technologies · Powered by Codezela Career Accelerator (CCA)

StarsForksIssuesMIT LicenseNext.js 16React 19TypeScriptTailwind CSS v4


Table of Contents


Overview

Commons by Codezela is a moderation-first, full-stack content management and article publishing platform. It empowers researchers, writers, and technical authors to publish, discover, and engage with articles through a governed workflow — complete with editorial review, role-based access, and full audit transparency.

The platform is designed to bridge the gap between casual blogging and formal academic publishing — giving individual authors the freedom to write while ensuring every published piece meets community standards through a structured moderation pipeline.

Whether you're running an internal knowledge base, a community research journal, or a technical blog with multiple contributors, Commons provides the infrastructure to manage content at scale with integrity.


Key Features

Content Publishing

  • Rich Text + Markdown — Dual-mode editor with live preview, powered by Tiptap v3
  • Full Article Lifecycle — Draft → Pending Review → Published / Rejected / Archived
  • Cover Images — Upload cover art for every article (5 MB max; JPEG, PNG, WebP, GIF, SVG)
  • Featured Articles — Spotlight up to 3 articles in a curated carousel with drag-to-reorder
  • Full-Text Search — PostgreSQL tsvector/tsquery indexed search with weighted title + content ranking
  • Reading Time — Auto-calculated at ~220 words per minute
  • Related Articles — Automatic same-category suggestions on article pages

Moderation & Governance

  • Moderation Queue — Dedicated review interface for pending submissions
  • Moderator Feedback — Rejection notes sent back to authors for revision
  • Re-Moderation on Edit — Published article edits trigger re-review
  • Comprehensive Audit Trail — Every admin and moderator action is logged with structured metadata
  • Policy Pages — Built-in Moderation Policy, Privacy Policy, Terms of Use, and Reporting pages

User Management & Auth

  • Three Roles — Reader, Moderator, Admin with granular permission boundaries
  • Email & Password — Secure sign-up/login with bcrypt hashing (8–128 char passwords)
  • Email Verification — Automatic verification email on sign-up with 24-hour expiry and auto sign-in after verification
  • OAuth — Google and GitHub social login
  • Password Reset — Email-based reset flow via Brevo (Sendinblue) transactional API
  • Session Management — 7-day sessions, 1-day refresh age, 5-minute cookie cache
  • User Banning — Admins can ban users with a reason and optional expiry date

Taxonomy

  • Categories — Staff-managed groupings with merge and delete support
  • Tags — Community-driven with moderation: readers submit tags → staff approve/reject
  • Article–Tag Many-to-Many — Flexible tagging with per-user permission filtering

Engagement

  • Four Reaction Types — 👍 Like, 💡 Insightful, ❤️ Inspiring, 🤔 Curious
  • Toggle Reactions — One reaction per user per article; click again to remove
  • Synthesized Audio Feedback — Web Audio API generates unique tones per reaction type
  • Haptic Feedback — Mobile vibration patterns via the web-haptics library
  • Author Profiles — Public author pages with article archive and category/tag filters

Developer Experience

  • TypeScript Strict Mode — Full type safety across the stack
  • Server Actions — All mutations via Next.js Server Actions; no REST API layer
  • Parameterized SQL — Direct PostgreSQL queries via pg with injection protection
  • Rate Limiting — Token-bucket rate limiting on all user-facing mutations
  • Automatic Sitemap & Robots — Dynamic XML sitemap with change frequency and priority

Tech Stack

LayerTechnologyVersion
FrameworkNext.js (App Router)16.1.6
LanguageTypeScript (strict mode)5.9.3
UI LibraryReact19.2.3
StylingTailwind CSS with CSS variablesv4
Componentsshadcn/ui (base-nova style)v4
IconsLucide React0.577.0
Rich Text EditorTiptap with 25+ extensionsv3
AuthenticationBetter Auth1.5.4
DatabasePostgreSQL (local or Supabase)17+
File StorageCloudflare R2 / Local fallback
EmailBrevo (transactional SMTP API)
ValidationZodv4
NotificationsSonner
MarkdownRemark / Rehype pipeline
DiagramsMermaid
Date Formattingdate-fns
Hapticsweb-haptics

Architecture

Commons follows the Next.js App Router paradigm with a clear separation of concerns:

┌──────────────────────────────────────────────────────────────────┐
│ Browser / Client │
│ │
│ React 19 RSC + Client Components ←→ shadcn/ui + Tiptap │
└───────────────────────┬──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ Next.js 16 App Router │
│ │
│ Server Components (default) API Routes │
│ ├── Data fetching (direct SQL) ├── /api/auth/* │
│ ├── Metadata generation ├── /api/upload │
│ └── Layout composition ├── /api/uploads/* │
│ └── /api/seed │
│ Server Actions (lib/actions/) │
│ ├── articles.ts (CRUD + moderation) │
│ ├── taxonomy.ts (categories + tags) │
│ ├── featured.ts (featured articles) │
│ ├── reactions.ts (article reactions) │
│ ├── users.ts (user management) │
│ └── audit.ts (audit log queries) │
└───────────────────────┬──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ Data Layer │
│ │
│ lib/db.ts ← pg Pool (parameterized queries) │
│ lib/auth.ts ← Better Auth (sessions + OAuth) │
│ lib/authz.ts ← Authorization guards │
│ lib/rate-limit.ts ← Token-bucket rate limiting │
│ lib/audit-log.ts ← Structured audit logging │
│ lib/r2.ts ← Cloudflare R2 (S3-compatible storage) │
│ lib/email.ts ← Brevo transactional email (verification + reset) │
└───────────────────────┬──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ PostgreSQL Database │
│ │
│ user · session · account · verification │
│ article · category · tag · article_tag │
│ article_reaction · admin_audit_log · rate_limit_bucket │
│ │
│ ✦ Full-text search (tsvector/GIN index) │
│ ✦ Auto-audit triggers (signup, login, logout, password reset, email verification) │
│ ✦ Database-level role constraints │
└──────────────────────────────────────────────────────────────────┘

Key design decisions:

  • React Server Components first"use client" only where browser APIs or interactivity is needed
  • No REST API layer for mutations — All data writes go through Next.js Server Actions
  • Direct SQL — No ORM; the pg library with parameterized queries for full control and safety
  • Session-based auth — HTTP-only cookies with 5-minute cache for performance
  • Database-level audit triggers — Signup, login, logout, and password changes are automatically logged at the PostgreSQL level

Getting Started

Prerequisites

ToolMinimum VersionNotes
Node.js18+LTS recommended
npm9+Comes with Node.js
PostgreSQL14+17 recommended; or use Supabase

macOS:

brew install node postgresql@17
brew services start postgresql@17

Linux (Debian/Ubuntu):

sudo apt install nodejs npm postgresql
sudo systemctl start postgresql

Quick Setup (Automated)

The project includes an automated setup script that handles everything:

git clone https://github.com/codezelat/commons-by-codezela.git
cd commons-by-codezela
chmod +x scripts/setup-local.sh
./scripts/setup-local.sh

The script will:

  1. Verify that PostgreSQL is installed and running
  2. Create the commons database if it doesn't exist
  3. Generate .env.local from .env.example with auto-detected connection strings
  4. Generate a random BETTER_AUTH_SECRET
  5. Install all npm dependencies
  6. Push the database schema

After the script completes:

npm run dev # Start the dev server
npm run db:seed # Seed the admin user (in another terminal)

Manual Setup

1. Clone the repository:

git clone https://github.com/codezelat/commons-by-codezela.git
cd commons-by-codezela

2. Install dependencies:

npm install

3. Create your environment file:

cp .env.example .env.local

4. Configure your .env.local:

# Generate a secretBETTER_AUTH_SECRET=$(openssl rand -base64 32)BETTER_AUTH_URL=http://localhost:3000NEXT_PUBLIC_APP_URL=http://localhost:3000# Local PostgreSQLDB_PROVIDER=localDATABASE_URL_LOCAL=postgresql://postgres:postgres@localhost:5432/commons

5. Create the database:

createdb commons

6. Push the schema:

npm run db:push

Database Seeding

Seed the initial admin user after the dev server is running:

# Start the server first
npm run dev
# In another terminal
npm run db:seed

This creates the admin account:

FieldValue
Emailinfo@codezela.com
Passwordpassword
Roleadmin

Important: Change the admin password immediately after first login in production.

Running the Dev Server

npm run dev

Open http://localhost:3000 to see the application.


Environment Variables

Copy .env.example to .env.local and configure the following:

Required

VariableDescription
BETTER_AUTH_SECRETSession encryption key. Generate with openssl rand -base64 32
BETTER_AUTH_URLPublic URL of the application (e.g., http://localhost:3000)
NEXT_PUBLIC_APP_URLPublic-facing base URL, used for metadata and sitemap generation
DB_PROVIDERlocal or supabase — determines which DATABASE_URL_* to use
DATABASE_URL_LOCALPostgreSQL connection string for local development

Database (Supabase)

VariableDescription
DATABASE_URL_SUPABASE_POOLERSupabase pooler URL (recommended for Vercel/free tier and IPv4-only setups)
DATABASE_URL_SUPABASEDirect Supabase database URL (db.<project-ref>.supabase.co)

Supabase Client (Optional)

VariableDescription
NEXT_PUBLIC_SUPABASE_URLProject URL (https://<ref>.supabase.co)
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEYSupabase publishable (anon) key

OAuth Providers (Optional)

VariableDescription
GOOGLE_CLIENT_IDGoogle OAuth 2.0 client ID
GOOGLE_CLIENT_SECRETGoogle OAuth 2.0 client secret
GITHUB_CLIENT_IDGitHub OAuth app client ID
GITHUB_CLIENT_SECRETGitHub OAuth app client secret

Cloudflare R2 Storage (Optional)

VariableDescription
R2_ACCOUNT_IDCloudflare account ID
R2_ACCESS_KEY_IDR2 API token access key
R2_SECRET_ACCESS_KEYR2 API token secret key
R2_BUCKET_NAMER2 bucket name (default: commons-uploads)
R2_PUBLIC_URLPublic URL prefix for R2 assets (required when R2 is enabled)

Brevo Email (Optional in Development)

VariableDescription
BREVO_API_KEYBrevo (Sendinblue) transactional API key
BREVO_SENDER_EMAILSender email address for transactional emails
BREVO_SENDER_NAMESender display name (default: Commons by Codezela Technologies)
BREVO_REPLY_TO_EMAILReply-to email address
BREVO_REPLY_TO_NAMEReply-to display name

Note: In development, if Brevo credentials are missing, password reset and email verification URLs are logged to the console instead of sent via email.


Database Schema

The database is defined in lib/schema.sql and consists of the following tables:

Core Tables

TableDescription
userUsers with email, name, image, role (admin/moderator/reader), ban status
sessionToken-based sessions with IP address and user agent tracking
accountOAuth provider accounts linked to users
verificationEmail verification and password reset tokens
articleArticles with JSONB content, generated tsvector for full-text search, SEO fields, moderation status
categoryArticle categories with name, slug, and description
tagTags with moderation status (approved/pending/rejected), creator/reviewer attribution
article_tagMany-to-many junction between articles and tags
article_reactionOne reaction per user per article (type: like/insightful/inspiring/curious)
admin_audit_logComprehensive audit trail with actor, action, target, and JSONB metadata
rate_limit_bucketToken-bucket rate limiting counters per key and time window

Indexes

The schema includes targeted indexes for performance:

  • idx_article_search — GIN index on search_vector for full-text search
  • idx_article_status — B-tree on status for filtering by publication state
  • idx_article_author — B-tree on author_id for author-specific queries
  • idx_article_category — B-tree on category_id for category filtering
  • idx_article_featured — Partial index on (is_featured, featured_order) where is_featured = TRUE
  • idx_article_published_at — Partial index on published_at DESC where status = 'published'
  • idx_article_slug — B-tree on slug for URL lookups
  • idx_article_pending_updated — Partial index on updated_at DESC where status = 'pending'
  • idx_tag_status_created — Composite index on (status, created_at DESC) for moderation queue
  • idx_article_reaction_article — B-tree on article_id for efficient reaction aggregation
  • idx_admin_audit_log_created_at — B-tree on created_at DESC for chronological audit browsing

Database Triggers

PostgreSQL triggers automatically log authentication events:

TriggerEventLogged Action
trg_audit_auth_signupINSERT on userauth.signup
trg_audit_session_createdINSERT on sessionauth.session.created
trg_audit_session_revokedDELETE on sessionauth.session.revoked
trg_audit_password_updatedUPDATE on account (password change)auth.password.updated
trg_audit_verification_requestedINSERT on verificationauth.verification.requested

Authentication

Authentication is handled by Better Auth v1.5, configured in lib/auth.ts.

Sign-Up & Login

  • Email/Password — 8–128 character passwords, bcrypt hashed
  • Google OAuth — One-click sign-in with Google accounts
  • GitHub OAuth — One-click sign-in with GitHub accounts

Email Verification

  • A verification email is automatically sent on sign-up
  • The verification link expires after 24 hours
  • Users are automatically signed in after successful verification
  • Branded HTML email template with fallback plain-text version

Password Reset

  • Users request a reset via the Forgot Password page
  • A reset link is sent via the Brevo transactional email API
  • The link directs to the Reset Password page where the user sets a new password
  • In development mode without Brevo credentials, the reset URL is logged to the console

Session Management

SettingValue
Session expiry7 days
Refresh age1 day
Cookie cache5 minutes
Secure cookiesEnabled in production
Rate limit30 requests per 60 seconds per IP

Auth Pages

PageRouteDescription
Login/loginEmail/password + OAuth login
Sign Up/signupNew account registration
Forgot Password/forgot-passwordRequest password reset email
Reset Password/reset-passwordSet new password via reset token

Role-Based Access Control (RBAC)

Commons implements a three-tier role system defined in lib/roles.ts and enforced by lib/authz.ts:

Roles

RoleDescription
ReaderDefault role. Can create articles, submit for review, submit new tags. Only sees own content and approved public content.
ModeratorCan review and approve/reject articles and tags. Can manage categories, featured articles. Sees all articles regardless of author.
AdminFull platform access. All Moderator privileges plus user management (role changes, bans), audit log access, and site configuration.

Permission Matrix

ActionReaderModeratorAdmin
Create articles
Edit own articles
Edit any article
Submit for review
Publish directly
Approve/reject articles
Submit new tags
Approve/reject tags
Create categories
Manage featured articles
View moderation queue
Manage users
View audit logs
Change user roles
Ban/unban users

Authorization Guards

The platform provides several server-side authorization utilities:

  • getOptionalSession() — Returns the current session or null
  • requireSession() — Throws "Unauthorized" if no session exists
  • requireStaffSession() — Requires Moderator or Admin role
  • requireAdminSession() — Requires Admin role
  • canManageArticle(role, userId, authorId) — Staff can manage any article; readers only their own

Article Lifecycle & Moderation

Articles go through a structured lifecycle from creation to publication:

 ┌─────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────┐ Submit ┌──────────┐ Approve ┌─────────┤
│ │ Draft │ ──────────→ │ Pending │ ───────────→ │Published│
│ └─────────┘ └──────────┘ └─────────┘
│ ↑ │ │
│ │ │ Reject │ Edit
│ │ ▼ │ (by reader)
│ │ ┌──────────┐ │
│ └──────────────── │ Rejected │ │
│ (revise & retry) └──────────┘ │
│ │
│ ┌──────────┐ │
│ │ Archived │ ←─────────────────┘
│ └──────────┘ (staff action)
└─────────────────────────────────────────────────────────────────┘

Statuses

StatusDescription
DraftWork in progress. Only visible to the author and staff.
PendingSubmitted for publication. Awaiting moderator review.
PublishedLive and visible to the public. Appears in search, sitemap, and feeds.
RejectedReturned to the author with moderator feedback. Reverts to draft for revision.
ArchivedManually removed from public view by staff.

Moderation Features

  • Review Queue — Moderators see all pending articles sorted by submission date
  • Moderator Notes — When rejecting, moderators provide feedback explaining why
  • Reviewer Attribution — Every moderation decision records who reviewed and when
  • Re-Moderation — When a reader edits a published article, its status reverts to pending for re-review
  • Force-Publish — Staff members can publish their own articles without going through the queue

Rate Limits

ActionReader LimitStaff Limit
Article creates/updates40 per hour180 per hour
Article submissions (pending)10 per dayUnlimited
Tag submissions25 per day120 per day
File uploads25 per minute, 250 per hour25 per minute, 250 per hour

Rich Text Editor

The article editor is built on Tiptap v3 with a comprehensive extension set, implemented in components/editor/block-editor.tsx.

Editor Modes

The editor supports two modes, switchable via tabs:

ModeDescription
Rich TextWYSIWYG editing with Tiptap, full formatting toolbar
MarkdownRaw Markdown editing with live preview and Mermaid diagram rendering

Tiptap Extensions

The following extensions are loaded:

ExtensionCapability
StarterKitParagraphs, bold, italic, strikethrough, code, blockquote, bullet/ordered lists, hard break, horizontal rule, headings (H1–H6)
UnderlineUnderline text formatting
LinkHyperlinks with rel="noopener noreferrer nofollow"
ImageInline images with alignment (left/center/right) and width control
TextAlignLeft, center, right, and justify alignment for headings and paragraphs
HighlightColored text highlighting
TypographySmart quotes, ellipsis, em-dash, and other typographic enhancements
SuperscriptSuperscript text
SubscriptSubscript text
TextStyleBase for color and other text styling
ColorText color picker
CodeBlockLowlightSyntax-highlighted code blocks with language selection (40+ languages via lowlight/common)
TableTables with resizable columns
TableRowTable row support
TableCellTable cell support
TableHeaderTable header cells
PlaceholderPlaceholder text: "Start writing your article..."
MarkdownBidirectional Markdown ↔ ProseMirror conversion via tiptap-markdown

Toolbar Features

The EditorToolbar provides:

  • Block type selector (paragraph, H1–H6, blockquote, code block)
  • Inline formatting (bold, italic, underline, strikethrough, code)
  • Text color picker
  • Highlight toggle
  • Superscript / subscript
  • Link insertion dialog
  • Image upload dialog with alignment and dimension controls
  • Table insertion (3-column, header/body rows)
  • Text alignment (left, center, right, justify)
  • Undo / redo
  • Lists (bullet and ordered)
  • Horizontal rule

Bubble Menu

A floating bubble menu appears when selecting an image, offering:

  • Alignment controls (left, center, right)
  • Replace image
  • Edit alt text
  • Delete image

Markdown Preview

When in Markdown mode, articles are processed through:

  1. remark-parse → Parse Markdown to AST
  2. remark-gfm → GitHub Flavored Markdown (tables, strikethrough, task lists)
  3. remark-breaks → Line breaks as <br>
  4. remark-github-blockquote-alert → GitHub-style alert blocks (Note, Tip, Important, Warning, Caution)
  5. remark-rehype → Convert to HTML AST
  6. rehype-raw → Allow raw HTML in Markdown
  7. rehype-sanitize → Sanitize HTML for XSS protection
  8. rehype-stringify → Render to HTML string
  9. Mermaid → Client-side rendering of Mermaid diagram blocks

Taxonomy System

Categories

Categories provide top-level organization for articles:

  • Staff-only management — Only Moderators and Admins can create, edit, or delete categories
  • Auto-slug generation — URL-safe slugs generated from category names
  • Merge support — Merge one category into another, transferring all articles
  • Delete safety — Deleting a category nullifies the category_id on associated articles
  • Article counts — Dashboard displays the number of articles per category

Tags

Tags enable fine-grained content classification with a moderation workflow:

  • Community-driven — Readers can submit new tags (status: pending)
  • Staff approval — Moderators review and approve or reject submitted tags
  • Auto-approval for staff — Tags created by Moderators/Admins are automatically approved
  • Moderation notes — Rejected tags include feedback explaining the rejection
  • Creator/reviewer attribution — Every tag records who created it and who reviewed it
  • Merge support — Merge one tag into another, transferring all article associations
  • Permission-filtered — Readers see only approved tags and their own pending submissions

Reactions & Engagement

Article Reactions

Readers can react to published articles with one of four types:

ReactionEmojiAudio Tone
Like👍Warm pop — sine wave sweeping 520 → 260 Hz
Insightful💡Bright bell — 880 Hz with 1320 Hz harmonics
Inspiring❤️Warm chord — E4/Ab4/C5 triadic chord
Curious🤔Rising glide — triangle wave sweeping 340 → 680 Hz
RemoveDescending blip — 440 → 220 Hz
  • One reaction per user per article (clicking the same reaction toggles it off)
  • Switching reactions replaces the previous one
  • Unauthenticated users see a prompt to sign in
  • Reaction counts are aggregated and displayed on article pages

Audio Feedback

Reactions trigger synthesized audio feedback via the Web Audio API (lib/use-reaction-sound.ts):

  • Each reaction type has a unique tone signature
  • Audio is generated client-side (no audio files needed)
  • Removing a reaction plays a descending blip cue

Haptic Feedback

On supported mobile devices, reactions trigger haptic vibration patterns via the web-haptics library:

  • Success pattern on adding a reaction
  • Warning pattern on removing a reaction
  • Selection pattern for UI interactions

File Upload System

Upload Flow

  1. User selects an image in the editor or cover image uploader
  2. Client-side validation (type + size check)
  3. POST /api/upload with FormData
  4. Server-side validation + rate limiting
  5. File stored in Cloudflare R2 (production) or local .uploads/ directory (development)
  6. Public URL returned to the client

Storage Backends

BackendWhen UsedConfiguration
Cloudflare R2When R2_ACCOUNT_ID is configuredS3-compatible API via @aws-sdk/client-s3
Local filesystemFallback when R2 is not configuredFiles stored in .uploads/, served via /api/uploads/*

Constraints

ConstraintValue
Maximum file size5 MB
Allowed typesJPEG, PNG, WebP, GIF, SVG
Rate limit (per user)25 uploads per minute, 250 per hour
Filename formatUUID-based random filenames

Image Component

The ManagedImage component handles rendering:

  • Uses Next.js Image with unoptimized for local/data URIs
  • Automatically remaps legacy /uploads/ paths to /api/uploads/
  • Responsive srcSet and sizing

SEO & Metadata

Per-Article SEO Fields

Every article supports customizable SEO metadata:

FieldDescription
seo_titleCustom title for search engines (falls back to article title)
seo_descriptionCustom meta description (auto-derived from content if blank)
seo_imageCustom Open Graph image (falls back to cover image)
canonical_urlCanonical URL for syndicated content
robots_noindexFlag to exclude from search engine indexing

Structured Data

Article pages include JSON-LD structured data compliant with the schema.org Article schema:

  • headline, description, image
  • datePublished, dateModified
  • author with name and url
  • publisher with organization details
  • Canonical URL

Open Graph & Twitter Cards

Full Open Graph metadata is generated for every article page:

  • og:title, og:description, og:image, og:type
  • og:url, og:site_name
  • Twitter card metadata

Sitemap

The app/sitemap.ts dynamically generates an XML sitemap including:

  • Homepage (priority: 1.0, changeFrequency: weekly)
  • Articles listing (priority: 0.9, changeFrequency: daily)
  • Every published article (with lastModified from updated_at)
  • Author profile pages
  • Legal pages (Moderation Policy, Privacy, Terms, Reporting)

Only articles with status = 'published' and robots_noindex = false are included.

Robots.txt

The app/robots.ts generates a standard robots.txt allowing all crawlers and pointing to the sitemap.


Audit Trail

Every significant action in the platform is logged to the admin_audit_log table:

Logged Actions

ActionTargetWhen
auth.signupuserNew user registration
auth.session.createdsessionUser login
auth.session.revokedsessionUser logout
auth.password.updatedaccountPassword change
auth.verification.requestedverificationEmail verification or password reset request
article.createdarticleNew article created
article.updatedarticleArticle content or metadata edited
article.status_changedarticleStatus transition (draft → pending → published, etc.)
featured.addedarticleArticle added to featured carousel
featured.removedarticleArticle removed from featured carousel
featured.reorderedarticleFeatured article order changed
tag.moderatedtagTag approved or rejected
user.role_changeduserUser role modified
user.banned / user.unbanneduserUser ban status changed

Audit Log Entry Structure

{
"id": "uuid",
"actor_id": "user-uuid",
"actor_role": "admin",
"action": "article.status_changed",
"target_type": "article",
"target_id": "article-uuid",
"target_label": "Article Title",
"metadata": {
"old_status": "pending",
"new_status": "published"
},
"created_at": "2026-01-15T10:30:00Z"
}

Dashboard Access

Admins can browse the audit log at /dashboard/audit with:

  • Full-text search across action types, actor names, and target labels
  • Filtering by action type and target type
  • Paginated, chronologically sorted results

Rate Limiting

Rate limiting is implemented via a token-bucket algorithm backed by PostgreSQL (lib/rate-limit.ts):

How It Works

  1. Each rate limit is identified by a hashed key (SHA-256 of user ID + action)
  2. Requests are grouped into time buckets (e.g., 1-hour or 1-day windows)
  3. Each request increments the counter via INSERT ... ON CONFLICT DO UPDATE
  4. If the counter exceeds the limit, a RateLimitError is thrown with a retryAfterSeconds value
  5. Stale buckets are cleaned up probabilistically (2% chance on each request)

Applied Limits

ActionKeyLimitWindow
Article writes (reader)article-write:{userId}401 hour
Article writes (staff)article-write:{userId}1801 hour
Article submissionsarticle-submit:{userId}101 day
Tag submissions (reader)tag-submit:{userId}251 day
Tag submissions (staff)tag-submit:{userId}1201 day
File uploads (per minute)upload-min:{userId}251 minute
File uploads (per hour)upload-hr:{userId}2501 hour
Auth requestsIP-based via Better Auth3060 seconds

Theming & Styling

Color System

The platform uses the OKLCh color space for perceptually uniform colors across light and dark modes. All colors are defined as CSS custom properties in app/globals.css:

  • Light and dark mode variants for every semantic color
  • Sidebar-specific color palette
  • Chart colors for data visualization
  • Destructive/error states

Dark Mode

Full dark mode support via CSS classes (.dark), with colors recalculated for equal perceptual contrast.

Typography

FontUsageSource
DM SansBody text, UI elementsGoogle Fonts
LoraDisplay headings, editorial accents (serif italic)Google Fonts
System MonoCode blocks, monospace contentSystem default

Animations

  • pub-fade-in — Fade-in animation for page transitions
  • pub-fade-in-d1 — Delayed staggered fade for sequential elements

Syntax Highlighting

Code blocks use a custom dark theme with carefully chosen colors:

  • Keywords — Pink (#f472b6)
  • Strings — Green (#86efac)
  • Numbers — Amber (#fbbf24)
  • Functions — Sky blue (#7dd3fc)
  • Comments — Slate (#94a3b8)
  • Meta/Doctags — Violet (#c4b5fd)

Component Library

Built on shadcn/ui with the base-nova preset. Components include:

Alert Dialog, Avatar, Badge, Button, Card, Checkbox, Command, Dialog, Dropdown Menu, Input, Label, Pagination, Popover, Scroll Area, Select, Separator, Sheet, Skeleton, Switch, Table, Tabs, Textarea, Toggle, Toggle Group, Tooltip, and more.


Project Structure

commons-by-codezela/
├── app/ # Next.js App Router
│ ├── globals.css # Global styles, OKLCh theme, syntax highlighting
│ ├── layout.tsx # Root layout (fonts, Toaster, TooltipProvider)
│ ├── page.tsx # Homepage — hero section
│ ├── robots.ts # Dynamic robots.txt generation
│ ├── sitemap.ts # Dynamic XML sitemap generation
│ │
│ ├── (auth)/ # Auth route group
│ │ ├── layout.tsx # Auth layout (centered, minimal)
│ │ ├── login/page.tsx # Login page
│ │ ├── signup/page.tsx # Registration page
│ │ ├── forgot-password/page.tsx # Password reset request
│ │ └── reset-password/page.tsx # Password reset form
│ │
│ ├── (dashboard)/ # Protected dashboard route group
│ │ ├── layout.tsx # Dashboard layout (sidebar + shell)
│ │ └── dashboard/
│ │ ├── page.tsx # Dashboard overview
│ │ ├── articles/ # Article management pages
│ │ ├── categories/ # Category CRUD
│ │ ├── tags/ # Tag management + moderation
│ │ ├── featured/ # Featured articles management
│ │ ├── moderation/ # Moderation queue
│ │ ├── users/ # User management (admin)
│ │ └── audit/ # Audit log viewer (admin)
│ │
│ ├── articles/ # Public article pages
│ │ ├── page.tsx # Article listing with search + filters
│ │ └── [slug]/page.tsx # Individual article page
│ │
│ ├── authors/[id]/page.tsx # Public author profile page
│ │
│ ├── api/ # API routes
│ │ ├── auth/[...all]/route.ts # Better Auth catch-all handler
│ │ ├── upload/route.ts # Image upload endpoint
│ │ ├── uploads/[...path]/route.ts # Local file serving endpoint
│ │ └── seed/route.ts # Admin user seeding endpoint
│ │
│ ├── moderation-policy/page.tsx # Moderation policy page
│ ├── privacy/page.tsx # Privacy policy page
│ ├── terms/page.tsx # Terms of use page
│ └── reporting/page.tsx # Content reporting page
│
├── components/
│ ├── ui/ # shadcn/ui base components
│ │ ├── button.tsx, card.tsx, dialog.tsx, ...
│ │ └── managed-image.tsx # Smart image component with URL normalization
│ │
│ ├── articles/ # Article feature components
│ │ ├── article-body.tsx # Article content renderer
│ │ ├── article-card.tsx # Article preview card
│ │ └── article-reactions.tsx # Reaction buttons + counts
│ │
│ ├── auth/ # Auth form components
│ │ ├── login-form.tsx
│ │ ├── signup-form.tsx
│ │ ├── forgot-password-form.tsx
│ │ └── reset-password-form.tsx
│ │
│ ├── dashboard/ # Dashboard feature components
│ │ ├── dashboard-shell.tsx # Dashboard layout shell (sidebar + nav)
│ │ ├── articles/ # Article management components
│ │ ├── categories/ # Category management components
│ │ ├── tags/ # Tag management components
│ │ ├── featured/ # Featured articles components
│ │ ├── moderation/ # Moderation queue components
│ │ ├── users/ # User management components
│ │ └── audit/ # Audit log components
│ │
│ ├── editor/ # Rich text editor
│ │ ├── block-editor.tsx # Main Tiptap editor component
│ │ ├── editor-toolbar.tsx # Formatting toolbar
│ │ ├── image-upload-dialog.tsx # Image upload modal
│ │ └── markdown-preview.tsx # Markdown preview renderer
│ │
│ └── site/ # Site-wide components
│ ├── public-shell.tsx # Public pages layout (header + footer)
│ ├── pub-theme-provider.tsx # Theme context provider
│ └── pub-theme-toggle.tsx # Dark/light mode toggle
│
├── lib/ # Core business logic
│ ├── db.ts # PostgreSQL Pool + query helpers
│ ├── auth.ts # Better Auth server configuration
│ ├── auth-client.ts # Better Auth client-side utilities
│ ├── authz.ts # Authorization guards (requireSession, etc.)
│ ├── roles.ts # Role definitions and helpers
│ ├── rate-limit.ts # Token-bucket rate limiting
│ ├── audit-log.ts # Structured audit log writer
│ ├── email.ts # Brevo transactional email (verification + reset)
│ ├── upload.ts # Client-side upload helpers + validation
│ ├── r2.ts # Cloudflare R2 storage integration
│ ├── local-upload.ts # Local filesystem upload fallback
│ ├── markdown.ts # Remark/Rehype Markdown processing pipeline
│ ├── editor-content.ts # Editor content type resolution
│ ├── article-metadata.ts # SEO metadata derivation for articles
│ ├── use-reaction-sound.ts # Web Audio API reaction sound synthesis
│ ├── utils.ts # Utility functions (cn, etc.)
│ ├── schema.sql # Full database schema DDL
│ │
│ └── actions/ # Next.js Server Actions
│ ├── articles.ts # Article CRUD, moderation, search
│ ├── taxonomy.ts # Category + tag management
│ ├── featured.ts # Featured articles operations
│ ├── reactions.ts # Article reaction toggle
│ ├── reaction-types.ts # Shared reaction type constants
│ ├── users.ts # User management (roles, bans)
│ └── audit.ts # Audit log queries
│
├── types/ # Shared TypeScript type definitions
│ └── markdown-it-task-lists.d.ts
│
├── scripts/ # Database management scripts
│ ├── setup-local.sh # Automated local setup script
│ ├── db-push.ts # Push schema.sql to database
│ ├── db-seed.ts # Seed admin user
│ └── db-reset.ts # Drop all tables + re-push + re-seed
│
├── public/uploads/ # Legacy public upload directory
│
├── next.config.ts # Next.js configuration
├── tailwind.config.ts # Tailwind CSS configuration
├── tsconfig.json # TypeScript (strict mode) configuration
├── eslint.config.mjs # ESLint configuration
├── postcss.config.mjs # PostCSS configuration
├── components.json # shadcn/ui configuration (base-nova)
├── package.json # Dependencies and scripts
└── .env.example # Environment variable template

Available Scripts

ScriptCommandDescription
Dev Servernpm run devStart Next.js development server on localhost:3000
Buildnpm run buildCreate production build
Startnpm run startStart production server
Lintnpm run lintRun ESLint
Push Schema (Local)npm run db:pushPush schema.sql to local PostgreSQL
Push Schema (Supabase)npm run db:push:livePush schema.sql to Supabase PostgreSQL
Seed (Local)npm run db:seedSeed admin user on local database
Seed (Supabase)npm run db:seed:liveSeed admin user on Supabase database
Reset Databasenpm run db:resetDrop all tables, re-push schema, re-seed
R2 Health Checknpm run r2:checkVerify R2 API access, upload/delete, and public URL

Deployment

Vercel (Recommended)

  1. Push your repository to GitHub
  2. Import the project in Vercel
  3. Configure the environment variables in the Vercel dashboard:
    • All variables from Environment Variables section
    • Set DB_PROVIDER=supabase and DATABASE_URL_SUPABASE_POOLER for Supabase
    • Set BETTER_AUTH_URL and NEXT_PUBLIC_APP_URL to your production domain
  4. Deploy

Supabase Database Setup

  1. Create a new project in Supabase
  2. Copy the pooler connection string from Settings → Database (free tier: use Supavisor/Pooler URL)
  3. Set DB_PROVIDER=supabase and DATABASE_URL_SUPABASE_POOLER in your environment
  4. Push the schema:
    npm run db:push:live
  5. Seed the admin user:
    npm run db:seed:live

Cloudflare R2 Setup

  1. Create a bucket (e.g., commons-up)
  2. Create API credentials:
    • Go to R2 → Manage R2 API tokens → Create API token
    • Permissions: Object Read + Object Write
    • Scope: at least the target bucket
    • Copy Access Key ID and Secret Access Key
  3. Configure environment variables:
    • R2_ACCOUNT_ID (from the S3 API endpoint host prefix)
    • R2_ACCESS_KEY_ID
    • R2_SECRET_ACCESS_KEY
    • R2_BUCKET_NAME
    • R2_PUBLIC_URL
  4. Configure public delivery (one option):
    • Enable Public Development URL for quick testing, then set R2_PUBLIC_URL to that URL
    • Or configure a Custom Domain and set R2_PUBLIC_URL to https://your-domain
  5. Verify end-to-end:
    npm run r2:check

Brevo Email Setup

  1. Create a Brevo account
  2. Generate a transactional API key
  3. Verify your sender email address
  4. Configure the BREVO_* environment variables

Contributing

Contributions are welcome! Here's how you can help:

  1. Fork the repository
  2. Create a branch for your feature or fix:
    git checkout -b feature/your-feature-name
  3. Make your changes following the project conventions:
    • TypeScript strict mode — type all function parameters and return values
    • Use @/ path alias for imports
    • Use Server Components by default; "use client" only when needed
    • Use Server Actions for mutations (in lib/actions/)
    • Use pg parameterized queries — never interpolate user input into SQL
    • Use Sonner for toast notifications
    • Use Zod for input validation
    • Use cn() from @/lib/utils for conditional class names
  4. Test your changes thoroughly
  5. Submit a Pull Request with a clear description of what you changed and why

License

This project is licensed under the MIT License. See the LICENSE file for the full text.


About Codezela

Codezela Technologies is a software engineering company dedicated to building open-source tools and platforms that empower developers, researchers, and creators.

Commons is powered by the Codezela Career Accelerator (CCA) — an initiative focused on accelerating technology careers through hands-on project experience, open-source contributions, and knowledge sharing.


Built with ❤️ by Codezela Technologies

About

Commons by Codezela is an open knowledge and publishing community by Codezela, built for students, undergraduates, developers, and curious builders to read, write, share, and grow through ideas that matter.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages