53595fdf94
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
651 lines
24 KiB
Markdown
651 lines
24 KiB
Markdown
# 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=
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Email Notifications (Brevo SMTP)
|
|
# -----------------------------------------------------------------------------
|
|
# SMTP relay for daily digest emails (deadline alerts, document expiry)
|
|
BREVO_SMTP_HOST=smtp-relay.brevo.com
|
|
BREVO_SMTP_PORT=587
|
|
BREVO_SMTP_USER= # Brevo SMTP login (from Brevo dashboard)
|
|
BREVO_SMTP_PASS= # Brevo SMTP key (from Brevo dashboard)
|
|
NOTIFICATION_FROM_EMAIL=noreply@beletage.ro
|
|
NOTIFICATION_FROM_NAME=Alerte Termene
|
|
NOTIFICATION_CRON_SECRET= # Random Bearer token for N8N → digest API auth
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# eTerra ANCPI (ParcelSync module — GIS data)
|
|
# -----------------------------------------------------------------------------
|
|
ETERRA_USERNAME= # eTerra login email
|
|
ETERRA_PASSWORD= # eTerra login password
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# ANCPI ePay (CF extract ordering)
|
|
# -----------------------------------------------------------------------------
|
|
ANCPI_USERNAME= # ePay login email (separate from eTerra)
|
|
ANCPI_PASSWORD= # ePay login password
|
|
ANCPI_BASE_URL=https://epay.ancpi.ro/epay
|
|
ANCPI_LOGIN_URL=https://oassl.ancpi.ro/openam/UI/Login
|
|
ANCPI_DEFAULT_SOLICITANT_ID=14452 # Beletage persoana juridica
|
|
MINIO_BUCKET_ANCPI=ancpi-documente # MinIO bucket for CF extract PDFs
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 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 <AIChatModule />;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 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<CompanyId, Company> = {
|
|
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).
|