Technology Stack
| Layer | Technology | Version/Details |
|---|---|---|
| Framework | Next.js (App Router) | 16.1.6 |
| Runtime | Node.js | 22 LTS |
| Language | TypeScript | 5.x |
| Database | PostgreSQL | 16 Alpine |
| ORM | Prisma | 5.22.0 |
| Authentication | Auth.js v5 | ^5.0.0-beta.25 |
| Object Storage | MinIO (S3-compatible) | latest |
| S3 Client | @aws-sdk/client-s3 | ^3.995.0 |
| Image Processing | Sharp | ^0.34.5 |
| Styling | Tailwind CSS | 4.x |
| Deployment | Docker, Traefik | latest |
Purpose and Philosophy
The Pathsala Municipal Board official portal serves as the digital gateway for citizens of Pathsala town to access municipal information, services, and governance transparency. The platform embodies the principle that government information should be accessible 24/7, anywhere, through a modern, responsive interface.
The project follows a modular monolith pattern built on Next.js App Router. Rather than splitting into microservices, all concerns—public citizen-facing pages, administrative dashboard, and REST-style API routes—are co-located in a single deployable container. This approach simplifies deployment, reduces infrastructure complexity, and maintains transaction consistency while still enabling independent scaling if needed later.
Core Design Principles
- Citizen-First Accessibility: The public-facing pages require no authentication, load fast on mobile devices, and provide clear navigation to essential information like news, services, board members, and contact details.
- Role-Based Content Management: The admin dashboard is protected by Auth.js credentials with role-based access. All content changes are logged for audit purposes, ensuring accountability in governance.
- Media-Rich Content: The system supports image galleries, video content, and document uploads through MinIO object storage, enabling the municipality to showcase projects, events, and important notices visually.
- Security by Design: The application fails at startup if MinIO credentials are not properly configured. This prevents silent misconfiguration that could lead to data loss or exposure.
- Single Source of Truth: All data models are defined in Prisma schema, serving as the definitive source for database structures and enabling type-safe queries throughout the application.
Architecture Deep Dive
Project Structure
pathsala-municipal-board/
├── prisma/
│ ├── schema.prisma # Database models (8 models, 3 domains)
│ └── seed.ts # Initial admin user seeding
├── scripts/
│ ├── audit-urls.cjs # Audit MinIO URLs in database
│ └── fix-urls.cjs # Migrate legacy MinIO URLs
├── src/
│ ├── app/
│ │ ├── (public)/ # Citizen-facing pages (no auth)
│ │ │ ├── page.tsx # Home page
│ │ │ ├── about/ # About the municipality
│ │ │ ├── administration/ # Governance & board members
│ │ │ ├── contact/ # Contact form
│ │ │ ├── gallery/ # Photo & video gallery
│ │ │ ├── news/ # News and public notices
│ │ │ ├── resources/ # Important links & documents
│ │ │ └── services/ # Municipal services info
│ │ ├── admin/
│ │ │ ├── login/ # Authentication page
│ │ │ └── (protected)/ # Admin dashboard
│ │ │ ├── dashboard/ # Overview & stats
│ │ │ ├── news/ # News management
│ │ │ ├── projects/ # Projects & tenders
│ │ │ ├── members/ # Board member management
│ │ │ ├── gallery/ # Media library
│ │ │ ├── links/ # Important links
│ │ │ ├── messages/ # Contact submissions
│ │ │ ├── storage/ # MinIO diagnostics
│ │ │ └── logs/ # Audit logs
│ │ └── api/
│ │ ├── auth/ # Auth.js handlers
│ │ ├── upload/ # File upload (S3 presigned)
│ │ └── media/ # Media library API
│ ├── components/ # Shared React components
│ ├── lib/
│ │ ├── minio.ts # S3 client & utilities
│ │ ├── prisma.ts # Prisma client singleton
│ │ └── audit.ts # Audit logging action
│ ├── auth.ts # Auth.js configuration
│ └── auth.config.ts # Route protection
├── docker-compose.yml # Full stack orchestration
├── Dockerfile # Multi-stage build
└── deploy.sh # Zero-downtime deployDatabase Schema
The Prisma schema defines 8 models across 3 distinct domains:
// Identity Domain
model User {
id String @id @default(cuid())
email String @unique
password String
role String @default("ADMIN")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
AuditLog AuditLog[]
}
// Content Domain
model Project {
id String @id @default(cuid())
name String
description String
category String @default("PROJECT") // PROJECT / TENDER / DOCUMENT
fileUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model News {
id String @id @default(cuid())
title String
description String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model GalleryItem {
id String @id @default(cuid())
title String?
mediaUrl String
type String // IMAGE / VIDEO / YOUTUBE
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model ImportantLink {
id String @id @default(cuid())
name String
icon String?
url String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// Citizen Engagement Domain
model Member {
id String @id @default(cuid())
name String
designation String
bio String?
profilePicture String?
email String?
contactNumber String?
category String // Board / Executive Officers / Staff
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model ContactMessage {
id String @id @default(cuid())
name String
email String
phone String?
message String
createdAt DateTime @default(now())
}
// Governance Domain
model AuditLog {
id String @id @default(cuid())
userId String?
user User? @relation(fields: [userId], references: [id])
actionType String // CREATE / UPDATE / DELETE
entityName String
entityId String?
changeMetadata String? // JSON string of diff
timestamp DateTime @default(now())
}Authentication System
The platform uses Auth.js v5 (formerly NextAuth) with a credentials provider:
// src/auth.ts
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
Credentials({
name: "Credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
// Validate against database
},
}),
],
callbacks: {
async jwt({ token, user }) {
// Add role to token
},
async session({ session, token }) {
// Add role to session
},
},
pages: {
signIn: "/admin/login",
},
});All protected admin routes use middleware-based authentication:
// src/auth.config.ts
export const { auth } = NextAuth(authConfig);
export default authMiddleware;
export const config = {
matcher: ["/admin/:path*"],
};Key Features and Functionality
1. Public Citizen Portal
The public-facing pages provide comprehensive municipal information:
Home Page (/)
- Hero section with municipal branding
- Quick access to important links
- Latest news and updates
- Service highlights
About Section (/about)
- Municipal history and vision
- Governance structure
- Contact information
Administration (/administration)
- Board members listing with profiles
- Executive officers and staff directory
- Organizational hierarchy
News (/news)
- Public notices and announcements
- News articles with descriptions
- Chronological ordering
Gallery (/gallery)
- Image galleries with lightbox
- Video content support
- YouTube embed integration
- Category-based organization
Services (/services)
- Municipal service information
- Online service requests (future)
- Service guides and procedures
Resources (/resources)
- Important links to external government portals
- Downloadable documents
- Tenders and project notices
Contact (/contact)
- Contact form for citizen inquiries
- Telegram bot notifications for admins
- Stored in database for response tracking
2. Admin Dashboard
Protected routes at /admin/* provide complete content management:
Dashboard (/admin/dashboard)
- Overview statistics
- Recent contact messages
- Quick actions
News Management (/admin/news)
- Create, edit, delete news articles
- Rich text descriptions
- Publication timestamps
Projects & Tenders (/admin/projects)
- Manage municipal projects
- Tender notices
- Document uploads
- Category classification (PROJECT/TENDER/DOCUMENT)
Board Members (/admin/members)
- CRUD for board members
- Profile pictures (MinIO storage)
- Designation and bio
- Contact information
- Category (Board/Executive Officers/Staff)
Gallery Management (/admin/gallery)
- Upload images and videos
- YouTube video embedding
- Title and description
- Delete functionality
Important Links (/admin/links)
- Manage external resource links
- Custom icons
- URL configuration
Contact Messages (/admin/messages)
- View all citizen submissions
- Mark as read/archived
- Reply tracking
Storage Diagnostics (/admin/storage)
- MinIO bucket status
- Storage usage metrics
- Connection health checks
Audit Logs (/admin/logs)
- Complete action history
- User attribution
- Change metadata (JSON diffs)
- Timestamp tracking
3. Media Storage System
MinIO provides S3-compatible object storage for all media:
// src/lib/minio.ts
import { S3Client, PutObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const s3Client = new S3Client({
endpoint: process.env.MINIO_ENDPOINT,
credentials: {
accessKeyId: process.env.MINIO_ROOT_USER,
secretAccessKey: process.env.MINIO_ROOT_PASSWORD,
},
forcePathStyle: true,
});Upload Flow:
- Admin selects file in dashboard
- Server generates presigned URL
- Browser uploads directly to MinIO
- URL stored in database
Storage Buckets:
pathsala-media- Main media storage- Anonymous download access enabled
- Public URL delivery via path or subdomain
4. Deployment Infrastructure
Docker Compose orchestrates four services:
services:
web:
image: pathsala-mb-website
ports: 3000
networks: [traefik-public, internal-network]
Traefik: ws.bajali.in
db:
image: postgres:16-alpine
internal-network only
minio:
image: minio/minio
networks: [traefik-public, internal-network]
Traefik: pmb-media-api.bajali.in, pmb-media-admin.bajali.in
minio-init:
image: minio/mc
One-shot bucket initializationTraefik Routing:
ws.bajali.in→ Next.js applicationpmb-media-api.bajali.in→ MinIO S3 APIpmb-media-admin.bajali.in→ MinIO consolews.bajali.in/pathsala-media/*→ MinIO via path prefix
Security Implementation
Authentication & Authorization
- Auth.js v5 with credentials provider
- Bcrypt password hashing
- Session-based authentication with secure cookies
- Role-based route protection (ADMIN role required for dashboard)
Infrastructure Security
- Database isolated on internal network (no external access)
- MinIO credentials validated at startup
- Environment variables for all secrets
- External Traefik network for public access only
- HTTPS enforced via Let's Encrypt
Audit Logging
- All admin actions logged to AuditLog table
- JSON change metadata for diff tracking
- User attribution on all actions
- Retention for governance accountability
Development and Operations
Local Development
# Full stack with Docker
docker compose up -d
# Native Node.js development
npm install
npm run db:generate
npm run db:migrate
npm run db:seed
npm run devDatabase Scripts
npm run db:migrate # Create new migration
npm run db:seed # Seed initial admin
npm run db:studio # Visual database browser
npm run db:push # Push schema changesProduction Deployment
The deploy.sh script provides automated deployment:
./deploy.sh --auto # Non-interactive: commit, push, rsync, rebuildMonitoring
# View application logs
docker logs -f pathsala-mb-website
# Check database
docker exec -it pathsala-db psql -U postgres -d pathsala_db
# MinIO health
curl https://pmb-media-api.bajali.in/minio/health/liveProduction URLs
| Service | URL |
|---|---|
| Portal | https://ws.bajali.in |
| MinIO API | https://pmb-media-api.bajali.in |
| MinIO Console | https://pmb-media-admin.bajali.in |
Lessons Learned and Best Practices
What Worked Well
- Modular Monolith: Single deployable unit simplifies operations while maintaining clear code organization
- Prisma Schema as Source of Truth: Type-safe database access throughout the application
- MinIO for Media: S3-compatible storage provides scalability without vendor lock-in
- Auth.js v5: Modern authentication with session management
- Audit Logging: Complete accountability for admin actions
Areas for Enhancement
- CI/CD Pipeline: Could implement GitHub Actions for automated testing and deployment
- Performance Monitoring: Add application performance monitoring (APM)
- Media Optimization: Automatic image resizing and format conversion
- Offline Support: Service worker for offline page viewing
Future Expansion
The architecture supports:
- Online Service Requests: Citizen portal for service applications
- Payment Integration: Online fee and tax collection
- Mobile Apps: React Native wrapper using same API
- Document Management: Version-controlled document repository
Conclusion
The Pathsala Municipal Board portal demonstrates a production-grade implementation of a government website using modern web technologies. The platform successfully provides citizens with 24/7 access to municipal information while giving administrators powerful content management tools.
The modular monolith architecture on Next.js 16 provides the right balance of simplicity and functionality for a municipal portal. With PostgreSQL for data persistence, MinIO for media storage, and comprehensive admin capabilities, the platform serves as a reliable digital infrastructure for local governance transparency.
Architecture Feedback
Spotted a potential optimization or antipattern? Let me know.