A centralized hub and identity management system for the All Things Linux (ATL) community and non-profit. Portal serves as the single source of truth and entry point for all ATL services, enabling users to create one ATL identity that provisions and manages access to services across multiple domains.
Portal provides unified access to:
- Free email services
- IRC and XMPP chat
- SSH pubnix spaces
- Web hosting
- Discord integration
- Wiki access
- Developer tools
- And more...
All services are accessible across multiple domains: atl.dev, atl.sh, atl.tools, atl.chat.
- Framework: React 19 + Next.js 16 (App Router)
- Language: TypeScript (strict mode)
- Styling: TailwindCSS 4 + Shadcn UI (Radix)
- Authentication: BetterAuth
- Database: DrizzleORM + PostgreSQL
- Validation: Zod
- State Management: TanStack Query (React Query)
- Internationalization: next-intl
- Code Quality: Biome + Ultracite
- Node.js >= 22.18.0 (LTS recommended, managed via
miseif available) - pnpm 10.27.0
- PostgreSQL database (PostgreSQL 18 recommended)
- Docker and Docker Compose (for local database setup)
# Install dependencies
pnpm install
# Set up environment variables
# Create a .env file with your configuration. Required variables include
# DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL, etc. Environment is
# validated via @t3-oss/env-nextjs in src/env.ts (extends module keys).
# Start PostgreSQL database (Docker Compose)
pnpm compose:db
# or: docker compose up -d portal-db
# Run database migrations
pnpm db:migrate
# (Optional) Seed database with sample data
pnpm db:seed
# Create initial admin user
pnpm create-admin# Start development server
pnpm dev
# Alternative development commands:
pnpm dev:turbo # Use Turbopack for faster builds
pnpm dev:https # Run with HTTPS enabled
pnpm dev:grab # React Grab + dev (cursor integration)
pnpm scan # Start dev server with React Scan (performance profiling)The application will be available at http://localhost:3000.
pnpm dev- Start development serverpnpm dev:turbo- Start dev server with Turbopackpnpm dev:https- Start dev server with HTTPSpnpm dev:grab- Start dev server with React Grab (cursor integration)pnpm scan- Start dev server with React Scan (performance profiling)pnpm build- Build for production (runs typegen then build)pnpm build:debug- Build with debug outputpnpm build:profile- Build with profilingpnpm build:prerender-debug- Build with prerender debuggingpnpm start- Start production server
pnpm check- Run linting and type checking (Ultracite)pnpm fix- Auto-fix linting and formatting issuespnpm type-check- Type check without lintingpnpm typegen- Generate Next.js types (run before build for full type checking)pnpm analyze- Analyze bundle sizepnpm analyze:output- Analyze and output bundle reportpnpm deduplicate- Deduplicate dependencies
pnpm test- Run test suitepnpm test:watch- Run tests in watch modepnpm test:coverage- Run tests with coverage report
pnpm db:generate- Generate migration files from schema changespnpm db:migrate- Run database migrationspnpm db:push- Push schema changes directly (dev only, no migrations)pnpm db:studio- Open Drizzle Studio (database GUI)pnpm db:seed- Seed database with sample datapnpm db:seed:reset- Reset and reseed database
pnpm auth:init-schema- Generate BetterAuth database schemapnpm create-admin- Create an admin user
pnpm compose:db- Start PostgreSQL container (portal-db)pnpm compose:db:down- Stop database stackpnpm compose:production- Start production profilepnpm compose:staging- Start staging profilepnpm compose:adminer- Start Adminer for DB managementpnpm compose:adminer:down/compose:production:down/compose:staging:down- Stop respective profiles
pnpm info- Show Next.js environment informationpnpm upgrade- Upgrade Next.js and dependenciespnpm telemetry:enable- Enable Next.js telemetrypnpm telemetry:disable- Disable Next.js telemetrypnpm release- Run semantic-release for versioning and changelog
src/
├── app/ # Next.js App Router
│ ├── (dashboard)/app/ # Protected dashboard routes (overview, admin, integrations, settings)
│ ├── .well-known/ # OAuth/OpenID discovery endpoints
│ ├── api/ # API routes (admin, auth, integrations, monitoring, user)
│ └── auth/ # Authentication pages and consent
├── components/ # Reusable React components
│ ├── ui/ # shadcn/ui components
│ └── layout/ # Layout (header, sidebar, navigation, page)
├── features/ # Feature modules (auth, admin, integrations, routing, user)
│ └── [name]/lib/ # Feature-specific logic (e.g. auth, integrations registry)
├── shared/ # Shared business logic and config
│ ├── api/ # API client, query keys, server queries
│ ├── config/ # Application configuration
│ ├── db/ # Database client, schema, migrations config
│ ├── types/ # Centralized types (auth, api, routes, common, email)
│ ├── utils/ # Constants, date, error, string helpers
│ ├── observability/ # Logging and monitoring
│ └── seo/ # Metadata, JSON-LD, robots, sitemap
├── hooks/ # Custom React hooks
├── i18n/ # Internationalization setup
├── styles/ # Global styles
├── env.ts # t3-env validated environment (extends module keys)
└── proxy.ts # Development proxy configuration
Path aliases: @/auth → src/features/auth/lib, @/db → src/shared/db, @/config → src/shared/config, @/ui/* → src/components/ui/*. See docs/PATH_ALIASES.md.
The project includes a Docker Compose configuration for local PostgreSQL development:
# Start PostgreSQL container
pnpm compose:db
# or: docker compose up -d portal-db
# The database will be available at:
# - Host: localhost
# - Port: 5432
# - User: postgres
# - Password: postgres
# - Database: portalUpdate your .env file with the database connection string:
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/portalThe project uses TypeScript path aliases for clean imports (@/auth → src/features/auth/lib, @/db → src/shared/db, @/config → src/shared/config). See docs/PATH_ALIASES.md.
import { auth, authClient } from "@/auth"; // Authentication
import { db } from "@/db"; // Database
import { BASE_URL } from "@/config"; // App config
import { Button } from "@/components/ui/button"; // UI components
import { usePermissions } from "@/hooks/use-permissions"; // Custom hooks- Barrel Exports: Core modules (
@/auth,@/db,@/config) use barrel exports for convenience - Direct Imports: UI components (
@/components/ui/*) and shared utils use direct imports for performance - Types: Centralized in
src/shared/types/(auth, api, routes, common, email); constants insrc/shared/utils/constants.ts - Environment: Validated via
@t3-oss/env-nextjsinsrc/env.ts, extending module-levelkeys()functions - Clear Boundaries: Strict separation between client and server code
- Type Safety: Full TypeScript coverage with strict mode enabled
Portal uses BetterAuth for authentication with role-based access control (RBAC):
- Roles:
user,staff,admin - Permissions: Granular permission system for fine-grained access control
- OAuth: Support for OAuth2 provider functionality (as both client and provider)
- Passkeys: Modern passwordless authentication support
- Email: Built-in email verification and password reset flows
- ORM: DrizzleORM for type-safe database queries
- Migrations: Version-controlled schema changes via Drizzle Kit
- Relations: Properly defined relationships between entities
- Schemas: Modular schema organization (auth, oauth, api-keys, etc.)
The project uses next-intl for multi-language support:
- Locale files: Located in
locale/directory - Routing: Automatic locale-based routing
- Translations: JSON-based translation files
- Git Hooks: Husky for pre-commit linting/formatting and commit message validation
- Version Management: Mise (optional) for Node.js version management
- Type Safety: Strict TypeScript with comprehensive type checking
- Linting: Biome for fast linting and formatting
- Code Quality: Ultracite for enhanced code quality checks
- Testing: Vitest for unit and integration tests
- CI/CD: GitHub Actions for automated testing and deployment
Portal follows a clear module organization pattern:
-
src/shared/: Shared business logic and configurationdb/- Database client, schema, and migration configapi/- API client, query keys, server queriesconfig/- Application configurationtypes/- Centralized types (auth, api, routes, common, email)utils/- Constants, date, error, string helpersobservability/- Logging and monitoringseo/- Metadata, JSON-LD, robots, sitemap
-
src/features/: Feature modules (auth, admin, integrations, routing, user)- Each feature may have
lib/,components/,hooks/,api/ - Auth lives at
@/auth→src/features/auth/lib - Integrations registry at
src/features/integrations/lib/core/registry.ts
- Each feature may have
-
src/components/: React componentsui/- Base shadcn/ui componentslayout/- Layout components (header, sidebar, navigation, page)
-
src/hooks/: Custom React hooks (e.g. use-mobile, use-permissions, use-image-preview) -
src/app/: Next.js App Routerapi/- API route handlers(dashboard)/app/- Protected routes (overview, admin, integrations, settings)auth/- Authentication and consent pages.well-known/- OAuth/OpenID discovery endpoints
Server-Only Code:
- Mark with
"use server"directive orimport "server-only" - API route handlers (
src/app/api/) - Server actions
- Database queries
- Auth utilities (
src/features/auth/lib/server-client.ts, imported via@/auth)
Client Code:
- Mark with
"use client"directive - React components with interactivity
- Client-side hooks
- Browser APIs (localStorage, window)
Pattern:
// Server-only
import "server-only"
import { auth } from "@/auth"
// Client component
"use client"
import { authClient } from "@/auth/client"Portal uses a registry pattern for integrations:
- Registry:
src/features/integrations/lib/core/registry.ts - Factory:
src/features/integrations/lib/core/factory.ts - Base Class:
BaseIntegrationinsrc/features/integrations/lib/core/base.ts - Registration: Integrations register themselves on module load
Adding a New Integration:
- Create integration module in
src/features/integrations/lib/[name]/ - Implement
BaseIntegrationinterface - Register in
src/features/integrations/lib/index.ts - Add environment variables in the integration’s
keys.ts
Response Format:
// Success response
Response.json({ ok: true, data: {...} })
// Error response
Response.json({ ok: false, error: "Error message" }, { status: 400 })Error Handling:
- Use
APIErrorclass for API errors - Use
handleAPIError()wrapper for consistent error responses - Errors are logged to Sentry automatically
Auth Guards:
requireAuth()- Requires any authenticated userrequireAdmin()- Requires admin rolerequireAdminOrStaff()- Requires admin or staff role
Example API Route:
export async function GET(request: NextRequest) {
try {
const { userId } = await requireAuth(request)
// ... business logic
return Response.json({ ok: true, data })
} catch (error) {
return handleAPIError(error)
}
}API Route Structure:
- RESTful conventions (
GET,POST,PATCH,DELETE) - Route handlers in
src/app/api/[resource]/route.ts - Nested resources:
src/app/api/[resource]/[id]/route.ts - Use DTOs to prevent exposing sensitive data
When to Create a Feature Module:
- Feature has multiple related components
- Feature has its own API routes
- Feature has complex business logic
- Feature needs isolation
Naming Conventions:
- Use kebab-case for file names (
user-management.tsx) - Use PascalCase for component names (
UserManagement) - Use camelCase for functions and variables
- Use UPPER_CASE for constants
Import Patterns:
- Prefer direct imports for performance:
import { Button } from "@/components/ui/button" - Use barrel exports for core modules:
import { auth } from "@/auth" - Group imports: external → internal → relative
Schema Organization:
- Modular schemas in
src/shared/db/schema/ - One file per domain (auth.ts, oauth.ts, api-keys.ts)
- Relations defined in
relations.ts - Use Drizzle ORM for type-safe queries
Migrations:
- Generate migrations:
pnpm db:generate - Review generated SQL before committing
- Run migrations:
pnpm db:migrate - Never edit migration files manually after generation
Query Patterns:
- Use DTOs (Data Transfer Objects) to select only needed fields
- Avoid selecting entire tables (
SELECT *) - Use transactions for multi-step operations
- Handle errors gracefully
Required Services:
- PostgreSQL 18+ (via Docker Compose)
- Node.js 22.18.0+
- pnpm 10.27.0
Environment Variables:
- Database connection (
DATABASE_URL) - BetterAuth configuration (
BETTER_AUTH_SECRET,BETTER_AUTH_URL) - Optional integrations (XMPP, Sentry, etc.)
Health Checks:
- Database:
docker compose ps portal-db - Application:
curl http://localhost:3000/api/health(if implemented)
GitHub Actions:
- Runs on Node.js 22.x
- Requires pnpm 10.27.0
- Caches pnpm dependencies
- Runs: lint, type-check, build, test
Pre-commit Checks:
- Linting and formatting (via lint-staged)
- Commit message validation (via commitlint)
Branch Protection:
- Require CI checks to pass
- Require PR reviews
- Enforce conventional commits
Build Requirements:
- Node.js >= 22.18.0
- pnpm 10.27.0
- PostgreSQL 18+
Environment Setup:
- Set all required environment variables
- Run database migrations before deployment
- Ensure database connection is stable
- Configure Sentry for error tracking
Post-Deployment:
- Verify health endpoints
- Check Sentry for errors
- Monitor database connections
- Verify authentication flows
- .github/CONTRIBUTING.md - Contribution guidelines and development workflow
- docs/ARCHITECTURE.md - Architecture overview and design patterns
- docs/API.md - REST API documentation and endpoints
- docs/COMPONENTS.md - Component conventions and usage
- docs/ACCESSIBILITY.md - Accessibility guidelines and best practices
- docs/TESTING.md - Testing patterns and best practices
- docs/CI_CD.md - CI/CD workflows and deployment guide
- docs/DEPLOYMENT.md - Deployment guide
- docs/INTEGRATIONS.md - Integration framework documentation
- docs/LOGGING.md - Logging and observability
- docs/PATH_ALIASES.md - TypeScript path alias usage
- docs/TSCONFIG.md - TypeScript configuration reference