# Configuration Guide > How ArchiTools is configured at build time, runtime, and per-module. --- ## Environment Variables All environment variables are defined in `.env.local` for development and injected via Docker for production. The `.env.example` file in the repository root documents every variable: ```bash # ============================================================================= # ArchiTools Environment Configuration # ============================================================================= # Copy this file to .env.local for development. # For Docker, pass variables via docker-compose.yml or Portainer. # ============================================================================= # ----------------------------------------------------------------------------- # Application # ----------------------------------------------------------------------------- NEXT_PUBLIC_APP_NAME=ArchiTools NEXT_PUBLIC_APP_URL=http://localhost:3000 # Version displayed in footer/about. Set by CI or manually. NEXT_PUBLIC_APP_VERSION=0.1.0 # ----------------------------------------------------------------------------- # Storage # ----------------------------------------------------------------------------- # Active storage adapter: 'localStorage' | 'api' NEXT_PUBLIC_STORAGE_ADAPTER=localStorage # REST API storage backend (required when STORAGE_ADAPTER=api) # STORAGE_API_URL=http://api.internal/storage # MinIO object storage (server-side only, for file/binary storage) # MINIO_ENDPOINT=minio.internal:9000 # MINIO_ACCESS_KEY= # MINIO_SECRET_KEY= # MINIO_BUCKET=architools # MINIO_USE_SSL=false # ----------------------------------------------------------------------------- # Authentication (future — Authentik SSO) # ----------------------------------------------------------------------------- # AUTHENTIK_URL=https://auth.internal # AUTHENTIK_CLIENT_ID= # AUTHENTIK_CLIENT_SECRET= # NEXTAUTH_URL=http://localhost:3000 # NEXTAUTH_SECRET= # ----------------------------------------------------------------------------- # Feature Flags # ----------------------------------------------------------------------------- # Comma-separated list of flag overrides. Format: flag_name=true/false # Example: NEXT_PUBLIC_FLAGS_OVERRIDE=module_ai_chat=true,module_password_vault=false NEXT_PUBLIC_FLAGS_OVERRIDE= # ----------------------------------------------------------------------------- # External Services # ----------------------------------------------------------------------------- # N8N webhook endpoint for automation triggers # N8N_WEBHOOK_URL=https://n8n.internal/webhook # Gitea API (for potential repo integration) # GITEA_URL=https://git.internal # GITEA_TOKEN= # ----------------------------------------------------------------------------- # External Tool Links (displayed on dashboard) # ----------------------------------------------------------------------------- # NEXT_PUBLIC_PORTAINER_URL=https://portainer.internal # NEXT_PUBLIC_DOZZLE_URL=https://dozzle.internal # NEXT_PUBLIC_NETDATA_URL=https://netdata.internal # NEXT_PUBLIC_UPTIME_KUMA_URL=https://uptime.internal # NEXT_PUBLIC_IT_TOOLS_URL=https://it-tools.internal # NEXT_PUBLIC_STIRLING_PDF_URL=https://pdf.internal # NEXT_PUBLIC_FILEBROWSER_URL=https://files.internal # NEXT_PUBLIC_N8N_URL=https://n8n.internal # NEXT_PUBLIC_GITEA_URL=https://git.internal # NEXT_PUBLIC_MINIO_CONSOLE_URL=https://minio-console.internal ``` ### Naming Rules | Prefix | Scope | Access | |---|---|---| | `NEXT_PUBLIC_` | Client + Server | Bundled into client JS. Never put secrets here. | | No prefix | Server only | Available in API routes, server components, middleware. | **Never put credentials, API keys, or secrets in `NEXT_PUBLIC_` variables.** They are embedded in the JavaScript bundle and visible to anyone with browser DevTools. --- ## Module Configuration **File:** `src/config/modules.ts` Defines the module registry. Every module in the system is declared here. Modules not in this registry do not exist to the platform. ```typescript // src/config/modules.ts import type { LucideIcon } from 'lucide-react'; interface ModuleDefinition { /** Unique module identifier. Matches the directory name under src/modules/. */ id: string; /** Romanian display name shown in navigation and headers. */ label: string; /** Short Romanian description for tooltips and dashboard cards. */ description: string; /** Lucide icon component reference. */ icon: LucideIcon; /** URL path segment. Module is accessible at /modules/{path}. */ path: string; /** Storage namespace. Must match STORAGE-LAYER.md namespace table. */ namespace: string; /** Feature flag that controls activation. References flags.ts. */ featureFlag: string; /** Default enabled state when no flag override exists. */ defaultEnabled: boolean; /** Minimum role required to see this module. */ minRole: Role; /** Sort order in navigation sidebar. Lower = higher. */ order: number; /** If true, module is loaded only when navigated to. */ lazy: boolean; /** Which companies this module is relevant to. Empty = all. */ companies: CompanyId[]; } export const MODULE_REGISTRY: ModuleDefinition[] = [ { id: 'dashboard', label: 'Panou Principal', description: 'Tablou de bord cu widget-uri și acces rapid', icon: LayoutDashboard, path: 'dashboard', namespace: 'dashboard', featureFlag: 'module_dashboard', defaultEnabled: true, minRole: 'viewer', order: 0, lazy: false, companies: [], }, { id: 'registratura', label: 'Registratură', description: 'Registru de intrări și ieșiri documente', icon: BookOpen, path: 'registratura', namespace: 'registratura', featureFlag: 'module_registratura', defaultEnabled: true, minRole: 'user', order: 10, lazy: true, companies: [], }, // ... remaining modules follow same pattern ]; ``` ### Adding a New Module 1. Create the module directory: `src/modules/{module-id}/`. 2. Add an entry to `MODULE_REGISTRY` in `src/config/modules.ts`. 3. Add a feature flag in `src/config/flags.ts`. 4. Add the namespace to the namespace table in the storage layer docs. 5. Create the App Router page: `src/app/modules/{path}/page.tsx`. 6. The navigation system and feature flag engine pick it up automatically. --- ## Feature Flag Configuration **File:** `src/config/flags.ts` ```typescript // src/config/flags.ts interface FeatureFlag { /** Flag identifier. Convention: module_{id} for module toggles, feature_{name} for features. */ id: string; /** Romanian description. */ description: string; /** Default value when no override exists. */ defaultValue: boolean; /** If true, flag is only visible in admin settings. */ adminOnly: boolean; /** If true, flag is experimental and shown with a warning badge. */ experimental: boolean; } export const FEATURE_FLAGS: FeatureFlag[] = [ // Module flags { id: 'module_dashboard', description: 'Panou principal', defaultValue: true, adminOnly: false, experimental: false }, { id: 'module_registratura', description: 'Registratură', defaultValue: true, adminOnly: false, experimental: false }, { id: 'module_email_signature', description: 'Generator semnătură email', defaultValue: true, adminOnly: false, experimental: false }, { id: 'module_word_xml', description: 'Generatoare Word XML', defaultValue: true, adminOnly: false, experimental: false }, { id: 'module_digital_signatures', description: 'Semnături și ștampile digitale', defaultValue: true, adminOnly: false, experimental: false }, { id: 'module_password_vault', description: 'Seif parole partajat', defaultValue: true, adminOnly: true, experimental: false }, { id: 'module_it_inventory', description: 'Inventar IT', defaultValue: true, adminOnly: false, experimental: false }, { id: 'module_address_book', description: 'Agendă de contacte', defaultValue: true, adminOnly: false, experimental: false }, { id: 'module_prompt_generator', description: 'Generator de prompturi AI', defaultValue: true, adminOnly: false, experimental: false }, { id: 'module_word_templates', description: 'Șabloane Word', defaultValue: true, adminOnly: false, experimental: false }, { id: 'module_tag_manager', description: 'Manager etichete', defaultValue: true, adminOnly: false, experimental: false }, { id: 'module_mini_utilities', description: 'Mini utilitare', defaultValue: true, adminOnly: false, experimental: false }, { id: 'module_ai_chat', description: 'Chat AI', defaultValue: false, adminOnly: false, experimental: true }, // Feature flags (non-module) { id: 'feature_dark_mode', description: 'Mod întunecat', defaultValue: true, adminOnly: false, experimental: false }, { id: 'feature_export_import', description: 'Export/import date', defaultValue: true, adminOnly: true, experimental: false }, { id: 'feature_cross_tab_sync', description: 'Sincronizare între tab-uri', defaultValue: true, adminOnly: false, experimental: false }, { id: 'feature_infra_links', description: 'Linkuri infrastructură pe panou',defaultValue: true, adminOnly: false, experimental: false }, ]; ``` ### Flag Resolution Order 1. **Environment override** (`NEXT_PUBLIC_FLAGS_OVERRIDE`): Highest priority. Parsed as comma-separated `key=value` pairs. 2. **Runtime override** (stored in `system` namespace under key `flag-overrides`): Set through admin UI. Persists across sessions. 3. **Default value** (`defaultValue` in `flags.ts`): Lowest priority. Used when no override exists. ```typescript // src/lib/flags/resolve.ts function resolveFlag(flagId: string): boolean { // 1. Environment override const envOverrides = parseEnvOverrides(process.env.NEXT_PUBLIC_FLAGS_OVERRIDE); if (flagId in envOverrides) return envOverrides[flagId]; // 2. Runtime override (from storage) const runtimeOverrides = getRuntimeOverrides(); // from system namespace if (flagId in runtimeOverrides) return runtimeOverrides[flagId]; // 3. Default const flag = FEATURE_FLAGS.find(f => f.id === flagId); return flag?.defaultValue ?? false; } ``` ### Usage in Components ```typescript import { useFeatureFlag } from '@/lib/flags/hooks'; function SomeComponent() { const aiChatEnabled = useFeatureFlag('module_ai_chat'); if (!aiChatEnabled) return null; return ; } ``` --- ## Navigation Configuration **File:** `src/config/navigation.ts` Navigation is derived from the module registry. No separate navigation config is maintained. The navigation system reads `MODULE_REGISTRY`, filters by feature flags and role, sorts by `order`, and renders the sidebar. ```typescript // src/config/navigation.ts import { MODULE_REGISTRY } from './modules'; interface NavGroup { label: string; modules: string[]; // Module IDs belonging to this group } /** * Groups organize modules in the sidebar. * Modules not listed in any group appear under "Altele" (Others). */ export const NAV_GROUPS: NavGroup[] = [ { label: 'Principal', modules: ['dashboard'], }, { label: 'Documente', modules: ['registratura', 'word-templates', 'word-xml', 'email-signature'], }, { label: 'Resurse', modules: ['address-book', 'digital-signatures', 'password-vault', 'it-inventory'], }, { label: 'Instrumente', modules: ['prompt-generator', 'ai-chat', 'mini-utilities'], }, { label: 'Administrare', modules: ['tag-manager'], }, ]; ``` ### External Links Infrastructure tool links are configured via environment variables and displayed in a separate sidebar section or dashboard widget: ```typescript // src/config/navigation.ts interface ExternalLink { label: string; envVar: string; // Environment variable holding the URL icon: LucideIcon; category: 'infra' | 'dev' | 'tools'; } export const EXTERNAL_LINKS: ExternalLink[] = [ { label: 'Portainer', envVar: 'NEXT_PUBLIC_PORTAINER_URL', icon: Container, category: 'infra' }, { label: 'Dozzle', envVar: 'NEXT_PUBLIC_DOZZLE_URL', icon: ScrollText, category: 'infra' }, { label: 'Netdata', envVar: 'NEXT_PUBLIC_NETDATA_URL', icon: Activity, category: 'infra' }, { label: 'Uptime Kuma', envVar: 'NEXT_PUBLIC_UPTIME_KUMA_URL', icon: HeartPulse, category: 'infra' }, { label: 'Gitea', envVar: 'NEXT_PUBLIC_GITEA_URL', icon: GitBranch, category: 'dev' }, { label: 'MinIO', envVar: 'NEXT_PUBLIC_MINIO_CONSOLE_URL', icon: Database, category: 'dev' }, { label: 'IT-Tools', envVar: 'NEXT_PUBLIC_IT_TOOLS_URL', icon: Wrench, category: 'tools' }, { label: 'Stirling PDF', envVar: 'NEXT_PUBLIC_STIRLING_PDF_URL', icon: FileText, category: 'tools' }, { label: 'N8N', envVar: 'NEXT_PUBLIC_N8N_URL', icon: Workflow, category: 'tools' }, { label: 'Filebrowser', envVar: 'NEXT_PUBLIC_FILEBROWSER_URL', icon: FolderOpen, category: 'tools' }, ]; ``` Links with no URL configured (empty env var) are hidden automatically. --- ## Company Configuration **File:** `src/config/companies.ts` Static company data. Updated rarely and only by developers. ```typescript // src/config/companies.ts import type { CompanyId, Company } from '@/types/company'; export const COMPANIES: Record = { beletage: { id: 'beletage', name: 'Beletage SRL', shortName: 'Beletage', cui: 'RO12345678', // replace with real CUI address: '...', email: 'office@beletage.ro', phone: '...', }, 'urban-switch': { id: 'urban-switch', name: 'Urban Switch SRL', shortName: 'Urban Switch', cui: 'RO23456789', address: '...', email: 'office@urbanswitch.ro', phone: '...', }, 'studii-de-teren': { id: 'studii-de-teren', name: 'Studii de Teren SRL', shortName: 'Studii de Teren', cui: 'RO34567890', address: '...', email: 'office@studiideteren.ro', phone: '...', }, group: { id: 'group', name: 'Grup Beletage', shortName: 'Grup', cui: '', }, }; export const COMPANY_IDS: CompanyId[] = ['beletage', 'urban-switch', 'studii-de-teren']; export const ALL_COMPANY_IDS: CompanyId[] = [...COMPANY_IDS, 'group']; ``` `COMPANY_IDS` excludes `'group'` for UI dropdowns where a real company selection is required. `ALL_COMPANY_IDS` includes it for contexts where "all companies" is valid. --- ## Theme Configuration **File:** `src/config/theme.ts` Theme tokens extend the shadcn/ui and Tailwind defaults. ```typescript // src/config/theme.ts export const THEME_CONFIG = { /** Default theme on first visit. User preference is stored in system namespace. */ defaultTheme: 'light' as 'light' | 'dark' | 'system', /** Company brand colors for badges, indicators, and charts. */ companyColors: { beletage: { primary: '#1E3A5F', accent: '#4A90D9' }, 'urban-switch': { primary: '#2D5F3E', accent: '#6BBF8A' }, 'studii-de-teren': { primary: '#5F4B1E', accent: '#D9A44A' }, group: { primary: '#374151', accent: '#6B7280' }, }, /** Tag category default colors. */ tagCategoryColors: { project: '#3B82F6', client: '#8B5CF6', phase: '#F59E0B', type: '#10B981', priority: '#EF4444', domain: '#6366F1', custom: '#6B7280', }, } as const; ``` Theme switching is handled by `next-themes` (shadcn/ui standard). The user's preference is stored in the `system` storage namespace under the key `theme-preference`. ### Tailwind Integration Company colors and tag colors are registered in `tailwind.config.ts` as extended colors: ```typescript // tailwind.config.ts (relevant excerpt) theme: { extend: { colors: { beletage: { DEFAULT: '#1E3A5F', accent: '#4A90D9' }, urbanswitch: { DEFAULT: '#2D5F3E', accent: '#6BBF8A' }, studiideteren: { DEFAULT: '#5F4B1E', accent: '#D9A44A' }, }, }, }, ``` --- ## Build-Time vs Runtime Configuration | Configuration Type | When Resolved | How Set | Changeable Without Rebuild | |---|---|---|---| | `NEXT_PUBLIC_*` env vars | Build time (bundled into JS) | `.env.local`, Docker build args | No (requires rebuild) | | Server-only env vars | Runtime (read on each request) | Docker env vars, `.env.local` | Yes | | Feature flag defaults | Build time (in `flags.ts`) | Source code | No | | Feature flag overrides (env) | Build time (via `NEXT_PUBLIC_FLAGS_OVERRIDE`) | Env var | No | | Feature flag overrides (runtime) | Runtime (from storage) | Admin UI | Yes | | Module registry | Build time (in `modules.ts`) | Source code | No | | Company data | Build time (in `companies.ts`) | Source code | No | | Theme preference | Runtime (from storage) | User toggle | Yes | | External tool URLs | Build time (via `NEXT_PUBLIC_*`) | Env vars | No | ### Making `NEXT_PUBLIC_` Variables Runtime-Configurable in Docker Next.js inlines `NEXT_PUBLIC_` variables at build time, which is problematic for Docker images that should be configurable at deploy time. Solution: **1. Build with placeholder values:** ```dockerfile # Dockerfile ARG NEXT_PUBLIC_APP_URL=__NEXT_PUBLIC_APP_URL__ ARG NEXT_PUBLIC_STORAGE_ADAPTER=__NEXT_PUBLIC_STORAGE_ADAPTER__ ``` **2. Replace at container start:** ```bash #!/bin/sh # docker/entrypoint.sh # Replace build-time placeholders with runtime environment values find /app/.next -type f -name '*.js' | while read file; do sed -i "s|__NEXT_PUBLIC_APP_URL__|${NEXT_PUBLIC_APP_URL:-http://localhost:3000}|g" "$file" sed -i "s|__NEXT_PUBLIC_STORAGE_ADAPTER__|${NEXT_PUBLIC_STORAGE_ADAPTER:-localStorage}|g" "$file" # ... repeat for each NEXT_PUBLIC_ variable done exec node server.js ``` **3. Use in docker-compose:** ```yaml # docker-compose.yml services: architools: image: architools:latest environment: - NEXT_PUBLIC_APP_URL=https://tools.internal - NEXT_PUBLIC_STORAGE_ADAPTER=api - STORAGE_API_URL=http://api:8080/storage - MINIO_ENDPOINT=minio:9000 - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY} - MINIO_SECRET_KEY=${MINIO_SECRET_KEY} ports: - "3000:3000" ``` --- ## Docker Environment Injection ### Development ```bash # .env.local (gitignored) NEXT_PUBLIC_APP_NAME=ArchiTools NEXT_PUBLIC_APP_URL=http://localhost:3000 NEXT_PUBLIC_STORAGE_ADAPTER=localStorage ``` ### Production (Docker Compose) ```yaml # docker-compose.yml version: '3.8' services: architools: build: context: . dockerfile: Dockerfile container_name: architools restart: unless-stopped ports: - "3000:3000" environment: - NODE_ENV=production - NEXT_PUBLIC_APP_NAME=ArchiTools - NEXT_PUBLIC_APP_URL=https://tools.internal - NEXT_PUBLIC_STORAGE_ADAPTER=api - STORAGE_API_URL=http://api:8080/storage - MINIO_ENDPOINT=minio:9000 - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY} - MINIO_SECRET_KEY=${MINIO_SECRET_KEY} - MINIO_BUCKET=architools # External links - NEXT_PUBLIC_PORTAINER_URL=https://portainer.internal - NEXT_PUBLIC_GITEA_URL=https://git.internal - NEXT_PUBLIC_N8N_URL=https://n8n.internal networks: - internal networks: internal: external: true ``` ### Production (Portainer) When deploying via Portainer, set environment variables in the container's Environment section. Sensitive values (MinIO keys, auth secrets) should use Portainer's secrets management rather than plain environment variables. --- ## Configuration Precedence Resolution order from lowest to highest priority: ``` 1. Source code defaults (flags.ts, modules.ts, companies.ts, theme.ts) ↑ overridden by 2. Environment variables (.env.local / Docker env) ↑ overridden by 3. Runtime overrides (admin UI → stored in system namespace) ``` If a conflict exists, the higher-priority source wins. Runtime overrides are only available for feature flags and user preferences (theme, sidebar state). Structural configuration (module registry, company data, navigation groups) is not runtime-overridable; it requires a code change and rebuild. --- ## Configuration File Index | File | Purpose | Changeable at Runtime | |---|---|---| | `src/config/modules.ts` | Module registry and metadata | No | | `src/config/flags.ts` | Feature flag definitions and defaults | Overridable via env/storage | | `src/config/navigation.ts` | Sidebar groups and external links | No | | `src/config/companies.ts` | Company master data | No | | `src/config/theme.ts` | Theme tokens and brand colors | Theme preference only | | `.env.local` | Development environment variables | N/A (dev only) | | `.env.example` | Documented variable template (committed) | N/A (reference) | | `docker-compose.yml` | Production environment variables | At deploy time | | `docker/entrypoint.sh` | Runtime placeholder replacement | At container start | --- ## Validation On application startup, the config system validates: 1. All required `NEXT_PUBLIC_` variables are set (not empty or placeholder). 2. `NEXT_PUBLIC_STORAGE_ADAPTER` is a known adapter type. 3. If adapter is `api`, `STORAGE_API_URL` is set. 4. If MinIO is configured, all three of `MINIO_ENDPOINT`, `MINIO_ACCESS_KEY`, and `MINIO_SECRET_KEY` are present. 5. Feature flag overrides parse correctly (no malformed entries). 6. Module IDs in `NAV_GROUPS` reference existing modules in `MODULE_REGISTRY`. Validation errors are logged to the console with `[ArchiTools Config]` prefix and do not crash the application. Missing optional config results in graceful degradation (e.g., external links not shown, MinIO features unavailable).