Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .claude/rules/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,13 @@ Key areas: JWT, MongoDB, GraphQL, email, security, static assets
|--------|---------|
| **Auth** | JWT authentication, refresh tokens, role-based access |
| **BetterAuth** | Modern auth integration (2FA, Passkey, Social) |
| **ErrorCode** | Centralized error codes with unique identifiers |
| **File** | File upload/download with GridFS storage |
| **User** | Core user management functionality |
| **HealthCheck** | Application health monitoring |
| **Migrate** | Database migration utilities |
| **SystemSetup** | Initial admin creation for fresh deployments |
| **Tus** | Resumable file uploads via tus.io protocol |
| **User** | Core user management functionality |

## Security Implementation

Expand Down
1 change: 1 addition & 0 deletions .claude/rules/configurable-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ This pattern is currently applied to:
| BetterAuth 2FA Plugin | `betterAuth.twoFactor` | Boolean Shorthand | `appName: 'Nest Server'` |
| BetterAuth Passkey Plugin | `betterAuth.passkey` | Boolean Shorthand | `rpName: 'Nest Server'` |
| BetterAuth Disable Sign-Up | `betterAuth.emailAndPassword.disableSignUp` | Explicit Boolean | `false` (sign-up enabled) |
| System Setup | `systemSetup` | Enabled by Default (when BetterAuth active) | `initialAdmin: undefined` |

## Checklist for New Configurable Features

Expand Down
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,13 @@ RATE_LIMIT_ENABLED=true
RATE_LIMIT_MAX=10
RATE_LIMIT_WINDOW_SECONDS=60
RATE_LIMIT_MESSAGE="Too many requests, please try again later."

# =============================================================================
# System Setup - Initial Admin (for automated deployments)
# =============================================================================
# Auto-creates the initial admin user on server start when zero users exist.
# Only takes effect on fresh deployments. Remove after first deployment.
# IMPORTANT: Use strong passwords and remove credentials from ENV after setup!
NSC__systemSetup__initialAdmin__email=admin@example.com
NSC__systemSetup__initialAdmin__password=YourSecurePassword123!
# NSC__systemSetup__initialAdmin__name=Admin
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ npm run reinit # Clean reinstall + tests + build
**Key Components:**
- `CoreModule` - Dynamic module with GraphQL, MongoDB, security
- `src/core/common/` - Decorators, helpers, interceptors, services
- `src/core/modules/` - Auth, BetterAuth, File, User, HealthCheck
- `src/core/modules/` - Auth, BetterAuth, ErrorCode, File, HealthCheck, Migrate, SystemSetup, Tus, User

See `.claude/rules/architecture.md` for detailed documentation.

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lenne.tech/nest-server",
"version": "11.13.4",
"version": "11.13.5",
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
"keywords": [
"node",
Expand Down
2 changes: 1 addition & 1 deletion spectaql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ servers:
info:
title: lT Nest Server
description: Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).
version: 11.13.4
version: 11.13.5
contact:
name: lenne.Tech GmbH
url: https://lenne.tech
Expand Down
23 changes: 14 additions & 9 deletions src/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { CoreBetterAuthModule } from './core/modules/better-auth/core-better-aut
import { CoreBetterAuthService } from './core/modules/better-auth/core-better-auth.service';
import { ErrorCodeModule } from './core/modules/error-code/error-code.module';
import { CoreHealthCheckModule } from './core/modules/health-check/core-health-check.module';
import { CoreSystemSetupModule } from './core/modules/system-setup/core-system-setup.module';

/**
* Core module (dynamic)
Expand Down Expand Up @@ -143,9 +144,9 @@ export class CoreModule implements NestModule {

// Build GraphQL driver configuration based on auth mode
const graphQlDriverConfig = isIamOnlyMode
? (isAutoRegisterDisabledEarly
? isAutoRegisterDisabledEarly
? this.buildLazyIamGraphQlDriver(cors, options)
: this.buildIamOnlyGraphQlDriver(cors, options))
: this.buildIamOnlyGraphQlDriver(cors, options)
: this.buildLegacyGraphQlDriver(AuthService, AuthModule, cors, options);

const config: IServerOptions = merge(
Expand Down Expand Up @@ -261,16 +262,14 @@ export class CoreModule implements NestModule {
// Determine if BetterAuth is explicitly disabled
// In IAM-only mode: enabled by default (undefined = true), only false or { enabled: false } disables
// In Legacy mode: disabled by default (undefined = false), must be explicitly enabled
const isExplicitlyDisabled = betterAuthConfig === false ||
(typeof betterAuthConfig === 'object' && betterAuthConfig?.enabled === false);
const isExplicitlyEnabled = betterAuthConfig === true ||
(typeof betterAuthConfig === 'object' && betterAuthConfig?.enabled !== false);
const isExplicitlyDisabled =
betterAuthConfig === false || (typeof betterAuthConfig === 'object' && betterAuthConfig?.enabled === false);
const isExplicitlyEnabled =
betterAuthConfig === true || (typeof betterAuthConfig === 'object' && betterAuthConfig?.enabled !== false);

// IAM-only mode: enabled unless explicitly disabled
// Legacy mode: enabled only if explicitly enabled
const isBetterAuthEnabled = isIamOnlyMode
? !isExplicitlyDisabled
: isExplicitlyEnabled;
const isBetterAuthEnabled = isIamOnlyMode ? !isExplicitlyDisabled : isExplicitlyEnabled;

const isAutoRegister = typeof betterAuthConfig === 'object' && betterAuthConfig?.autoRegister === true;
// autoRegister: false means the project imports its own BetterAuthModule separately
Expand Down Expand Up @@ -304,6 +303,12 @@ export class CoreModule implements NestModule {
}
}

// Add CoreSystemSetupModule when BetterAuth is active
// Enabled by default - disable explicitly via systemSetup: { enabled: false }
if (isBetterAuthEnabled && config.systemSetup?.enabled !== false) {
imports.push(CoreSystemSetupModule);
}

// Set exports
const exports: any[] = [ConfigService, EmailService, TemplateService, MailjetService];
if (!process.env.VITEST) {
Expand Down
76 changes: 76 additions & 0 deletions src/core/common/interfaces/server-options.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,39 @@ export interface IServerOptions {
path?: string;
};

/**
* System setup configuration for initial admin creation.
*
* When enabled, provides REST endpoints for creating the first admin user
* on a fresh deployment (zero users in the database).
*
* Enabled by default when BetterAuth is active. Explicitly disable with:
* - `{ enabled: false }`: Disabled
*
* Auto-creation via config/ENV (no REST call needed):
* - `{ initialAdmin: { email: '...', password: '...' } }`
* - `NSC__systemSetup__initialAdmin__email` + `NSC__systemSetup__initialAdmin__password`
*
* @since 11.14.0
*
* @example
* ```typescript
* // Enabled by default (no config needed when BetterAuth is active)
*
* // Disable explicitly
* systemSetup: { enabled: false },
*
* // Auto-create admin on server start (useful for Docker/CI)
* systemSetup: {
* initialAdmin: {
* email: process.env.INITIAL_ADMIN_EMAIL,
* password: process.env.INITIAL_ADMIN_PASSWORD,
* },
* },
* ```
*/
systemSetup?: ISystemSetup;

/**
* Templates
*/
Expand Down Expand Up @@ -1476,6 +1509,49 @@ export interface IServerOptions {
tus?: boolean | ITusConfig;
}

export interface ISystemSetup {
/**
* Whether system setup is enabled.
* @default true (when BetterAuth is enabled)
*/
enabled?: boolean;

/**
* Pre-configured initial admin credentials for automatic creation on server start.
*
* When set, the service will automatically create the initial admin user
* during application bootstrap if zero users exist. This is useful for
* automated deployments (Docker, CI/CD) where no manual REST call is possible.
*
* Can be provided via environment variables:
* - `NSC__systemSetup__initialAdmin__email`
* - `NSC__systemSetup__initialAdmin__password`
* - `NSC__systemSetup__initialAdmin__name` (optional)
*
* Security: Same zero-user guard applies - only works when no users exist.
*/
initialAdmin?: ISystemSetupInitialAdmin;
}

/**
* System setup configuration interface
*
* Follows the "presence implies enabled" pattern:
* - `undefined`: Disabled (default, backward compatible)
* - `{}`: Enabled with defaults
* - `{ enabled: false }`: Disabled explicitly
*
* @since 11.14.0
*/
export interface ISystemSetupInitialAdmin {
/** Email address for the initial admin user */
email: string;
/** Name of the initial admin user (defaults to email prefix) */
name?: string;
/** Password for the initial admin user (minimum 8 characters) */
password: string;
}

/**
* TUS Upload Configuration Interface
*
Expand Down
31 changes: 31 additions & 0 deletions src/core/modules/error-code/error-codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,37 @@ export const LtnsErrors = {
en: 'Legacy authentication is disabled. Please use the new authentication.',
},
},

// =====================================================
// System Setup Errors (LTNS_0050-LTNS_0059)
// =====================================================

SYSTEM_SETUP_NOT_AVAILABLE: {
code: 'LTNS_0050',
message: 'System setup not available - users already exist',
translations: {
de: 'System-Setup nicht verfügbar - es existieren bereits Benutzer.',
en: 'System setup not available - users already exist.',
},
},

SYSTEM_SETUP_DISABLED: {
code: 'LTNS_0051',
message: 'System setup is disabled',
translations: {
de: 'System-Setup ist deaktiviert.',
en: 'System setup is disabled.',
},
},

SYSTEM_SETUP_BETTERAUTH_REQUIRED: {
code: 'LTNS_0052',
message: 'System setup requires BetterAuth',
translations: {
de: 'System-Setup erfordert BetterAuth.',
en: 'System setup requires BetterAuth.',
},
},
} as const satisfies IErrorRegistry;
/* eslint-enable perfectionist/sort-objects */

Expand Down
107 changes: 107 additions & 0 deletions src/core/modules/system-setup/INTEGRATION-CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# System Setup Integration Checklist

**For initial admin creation on fresh deployments in projects using `@lenne.tech/nest-server`.**

> **Note:** System setup is **enabled by default** when BetterAuth is active. No explicit configuration is needed for the REST endpoints to work.

---

## Do You Need This Checklist?

| Scenario | Action Needed |
|----------|---------------|
| Fresh deployment needs initial admin creation | None - endpoints are available by default |
| Automated deployment (Docker/CI) needs auto-creation | Set ENV variables (Step 1) |
| Want to disable system setup | Set `systemSetup: { enabled: false }` in config |
| Custom setup logic (extra fields, notifications) | Step 2 (Custom Controller) |

---

## Reference Implementation

**Local (in your node_modules):**
```
node_modules/@lenne.tech/nest-server/src/core/modules/system-setup/
```

**GitHub:**
https://github.com/lenneTech/nest-server/tree/develop/src/core/modules/system-setup

---

## Step 1: Auto-Creation via ENV (Optional)

For automated deployments where no manual REST call is possible:

```bash
# .env or Docker environment
NSC__systemSetup__initialAdmin__email=admin@example.com
NSC__systemSetup__initialAdmin__password=YourSecurePassword123!
NSC__systemSetup__initialAdmin__name=Admin # optional
```

Or in `config.env.ts`:

```typescript
systemSetup: {
initialAdmin: {
email: process.env.INITIAL_ADMIN_EMAIL,
password: process.env.INITIAL_ADMIN_PASSWORD,
},
},
```

The admin is created automatically on server start when zero users exist.

**Security:** Remove credentials from ENV after the first successful deployment.

---

## Step 2: Custom Controller (Optional)

Only needed if you want to add extra validation, logging, or custom fields.

**Create:** `src/server/modules/system-setup/system-setup.controller.ts`

```typescript
import { Controller } from '@nestjs/common';
import { CoreSystemSetupController, Roles, RoleEnum } from '@lenne.tech/nest-server';

@Controller('api/system-setup')
@Roles(RoleEnum.ADMIN)
export class SystemSetupController extends CoreSystemSetupController {
// Override methods here for custom logic
}
```

> **Note:** The SystemSetup module auto-registers its controller via CoreModule. To use a custom controller, you would need to disable auto-registration and register your own module.

---

## Verification Checklist

- [ ] `npm run build` succeeds
- [ ] `npm test` passes
- [ ] `GET /api/system-setup/status` returns `{ needsSetup: true }` on empty database
- [ ] `POST /api/system-setup/init` creates admin user with correct role
- [ ] `GET /api/system-setup/status` returns `{ needsSetup: false }` after init
- [ ] `POST /api/system-setup/init` returns 403 when users already exist

---

## Common Mistakes

| Mistake | Symptom | Fix |
|---------|---------|-----|
| BetterAuth not enabled | 404 on endpoints or 403 on init | Ensure `betterAuth` is configured |
| Calling init with existing users | 403 "System setup not available" | Init only works on empty database |
| Password too short | 400 validation error | Password must be at least 8 characters |
| Missing ENV password | Auto-creation silently skipped | Set both `email` and `password` ENV vars |
| `systemSetup: { enabled: false }` in config | 404 on endpoints | Remove the explicit disable |

---

## Detailed Documentation

- **README.md:** `node_modules/@lenne.tech/nest-server/src/core/modules/system-setup/README.md`
- **GitHub:** https://github.com/lenneTech/nest-server/blob/develop/src/core/modules/system-setup/README.md
Loading