Skip to content

apolocine/mosta-setup

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@mostajs/setup

Reusable setup wizard module — multi-dialect DB configuration, .env.local writer, seed runner, module discovery.

npm version license

Part of the @mosta suite.


Table des matieres

  1. Installation
  2. Architecture du package
  3. Didacticiel : integrer @mostajs/setup dans une nouvelle app
  4. API Reference
  5. Les 13 dialectes supportes
  6. Systeme de modules
  7. Exemples avances
  8. Reconfiguration (post-installation)
  9. FAQ / Troubleshooting

1. Installation

npm install @mostajs/setup @mostajs/orm

@mostajs/orm est un peer dependency requis (il fournit les dialectes DB).


2. Architecture du package

@mostajs/setup
├── data/
│   ├── dialects.ts              # Metadata des 13 SGBD (icone, port, driver)
│   └── module-definitions.ts    # Liste statique des modules @mostajs/*
├── lib/
│   ├── setup.ts                 # needsSetup() + runInstall()
│   ├── compose-uri.ts           # Compose URI de connexion (mongo, pg, mysql...)
│   ├── db-test.ts               # Test de connexion ephemere
│   ├── env-writer.ts            # Ecriture/maj de .env.local
│   └── discover-modules.ts      # Decouverte npm dynamique
├── api/
│   ├── status.route.ts          # Factory GET  /api/setup/status
│   ├── test-db.route.ts         # Factory POST /api/setup/test-db
│   ├── install.route.ts         # Factory POST /api/setup/install
│   ├── detect-modules.route.ts  # Factory GET  /api/setup/detect-modules
│   ├── install-modules.route.ts # Factory POST /api/setup/install-modules
│   ├── reconfig.route.ts        # Factory GET+POST /api/setup/reconfig
│   └── upload-jar.route.ts     # Factory GET+POST+DELETE /api/setup/upload-jar
├── components/
│   ├── SetupWizard.tsx          # Wizard d'installation complet (6 etapes)
│   └── ReconfigPanel.tsx        # UI reconfiguration (modules + DB)
├── types/
│   └── index.ts                 # Tous les types TypeScript
└── index.ts                     # Barrel exports

Principe : le package fournit des factory functions qui retournent des handlers { GET } ou { POST }. Votre app Next.js les expose en une ligne par route.


3. Didacticiel : integrer @mostajs/setup dans une nouvelle app

Prerequis

  • Next.js 14+ (App Router)
  • Node.js >= 18
  • @mostajs/orm installe

Etape 1 — Installation des dependances

npm install @mostajs/setup @mostajs/orm bcryptjs
npm install -D @types/bcryptjs

Etape 2 — Configurer le setup (lib/setup.ts)

Ce fichier est le pont entre le package generique et votre application. Il definit :

  • Comment compter les utilisateurs (pour needsSetup)
  • Comment seeder les roles/permissions
  • Comment creer l'admin
  • Les seeds optionnels propres a votre app
// src/lib/setup.ts
import {
  needsSetup as _needsSetup,
  runInstall as _runInstall,
} from '@mostajs/setup/lib/setup'
import type {
  DialectType,
  MostaSetupConfig,
  SeedDefinition,
  InstallConfig,
} from '@mostajs/setup'

export type { DialectType }

// ─── needsSetup : verifie si la DB a 0 users ──────────────
export async function needsSetup(): Promise<boolean> {
  return _needsSetup(async () => {
    // Adaptez cette ligne a votre couche d'acces aux donnees
    const { userRepo } = await import('@/dal/service')
    const repo = await userRepo()
    return repo.count()
  })
}

// ─── Seeds optionnels (propres a votre app) ────────────────
const optionalSeeds: SeedDefinition[] = [
  {
    key: 'demoData',
    label: 'Donnees de demonstration',
    description: 'Quelques enregistrements pour tester',
    run: async () => {
      // Votre logique de seed
      const { productRepo } = await import('@/dal/service')
      const repo = await productRepo()
      await repo.create({ name: 'Produit A', price: 1000 })
      await repo.create({ name: 'Produit B', price: 2000 })
    },
  },
]

// ─── Configuration du setup ────────────────────────────────
const SETUP_CONFIG: MostaSetupConfig = {
  appName: 'Mon Application',
  defaultPort: 3000,

  // Callback pour seeder les roles et permissions
  seedRBAC: async () => {
    const { roleRepo, permissionRepo } = await import('@/dal/service')
    const pRepo = await permissionRepo()
    const rRepo = await roleRepo()

    // Creer les permissions
    const readPerm = await pRepo.upsert(
      { name: 'read' },
      { name: 'read', description: 'Lecture' },
    )
    const writePerm = await pRepo.upsert(
      { name: 'write' },
      { name: 'write', description: 'Ecriture' },
    )

    // Creer les roles avec permissions
    await rRepo.upsert(
      { name: 'admin' },
      { name: 'admin', description: 'Administrateur', permissions: [readPerm.id, writePerm.id] },
    )
    await rRepo.upsert(
      { name: 'viewer' },
      { name: 'viewer', description: 'Lecteur', permissions: [readPerm.id] },
    )
  },

  // Callback pour creer le premier administrateur
  // Le mot de passe est DEJA hashe par le package (bcrypt, 12 rounds)
  createAdmin: async ({ email, hashedPassword, firstName, lastName }) => {
    const { userRepo, roleRepo } = await import('@/dal/service')
    const uRepo = await userRepo()
    const rRepo = await roleRepo()

    const adminRole = await rRepo.findOne({ name: 'admin' })
    await uRepo.upsert(
      { email: email.toLowerCase() },
      {
        email: email.toLowerCase(),
        password: hashedPassword,
        firstName,
        lastName,
        roles: adminRole ? [adminRole.id] : [],
        status: 'active',
      },
    )
  },

  optionalSeeds,
}

// ─── runInstall : expose au route handler ──────────────────
export async function runInstall(config: InstallConfig) {
  return _runInstall(config, SETUP_CONFIG)
}

Points cles :

  • needsSetup() retourne true si 0 users en base → l'app doit s'installer
  • runInstall() execute les 6 etapes : ecriture .env.local, connexion DB, seed RBAC, creation admin, seeds optionnels
  • Le mot de passe admin est automatiquement hashe par le package (bcrypt, 12 rounds). Ne le hachez PAS vous-meme.
  • MOSTAJS_MODULES est ecrit dans .env.local si config.modules est fourni

Etape 3 — Creer les routes API

Creez 5 fichiers sous src/app/api/setup/ :

3.1 — Status (GET)

// src/app/api/setup/status/route.ts
import { createStatusHandler } from '@mostajs/setup'
import { needsSetup } from '@/lib/setup'

export const { GET } = createStatusHandler(needsSetup)

Reponse : { "needsSetup": true } ou { "needsSetup": false }

3.2 — Test connexion DB (POST)

// src/app/api/setup/test-db/route.ts
import { createTestDbHandler } from '@mostajs/setup'
import { needsSetup } from '@/lib/setup'

export const { POST } = createTestDbHandler(needsSetup)

Body attendu :

{
  "dialect": "postgres",
  "host": "localhost",
  "port": 5432,
  "name": "mydb",
  "user": "postgres",
  "password": "secret"
}

Reponse : { "ok": true } ou { "ok": false, "error": "..." }

3.3 — Installation (POST)

// src/app/api/setup/install/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { needsSetup, runInstall } from '@/lib/setup'
import { z } from 'zod'

const ALL_DIALECTS = [
  'mongodb', 'sqlite', 'postgres', 'mysql', 'mariadb',
  'oracle', 'mssql', 'cockroachdb', 'db2', 'hana',
  'hsqldb', 'spanner', 'sybase',
] as const

const installSchema = z.object({
  dialect: z.enum(ALL_DIALECTS),
  db: z.object({
    host: z.string().default('localhost'),
    port: z.number().int().min(0).max(65535).default(0),
    name: z.string().min(1),
    user: z.string().default(''),
    password: z.string().default(''),
  }),
  admin: z.object({
    email: z.string().email(),
    password: z.string().min(6),
    firstName: z.string().min(1),
    lastName: z.string().min(1),
  }),
  seed: z.record(z.boolean()).optional(),
  modules: z.array(z.string()).optional(),
})

export async function POST(req: NextRequest) {
  const setupNeeded = await needsSetup()
  if (!setupNeeded) {
    return NextResponse.json(
      { error: { code: 'FORBIDDEN', message: 'Installation deja effectuee' } },
      { status: 403 },
    )
  }

  const body = await req.json()
  const parsed = installSchema.safeParse(body)
  if (!parsed.success) {
    return NextResponse.json(
      { error: { code: 'VALIDATION_ERROR', details: parsed.error.flatten() } },
      { status: 400 },
    )
  }

  const result = await runInstall(parsed.data)
  if (!result.ok) {
    return NextResponse.json(
      { error: { code: 'INSTALL_ERROR', message: result.error } },
      { status: 500 },
    )
  }

  return NextResponse.json({
    data: { ok: true, needsRestart: result.needsRestart, seeded: result.seeded },
  })
}

3.4 — Detection des modules (GET)

// src/app/api/setup/detect-modules/route.ts
import { createDetectModulesHandler } from '@mostajs/setup'

export const { GET } = createDetectModulesHandler()

Reponse :

{
  "modules": [
    { "key": "orm", "packageName": "@mostajs/orm", "label": "ORM", "required": true, ... },
    { "key": "auth", "packageName": "@mostajs/auth", ... },
    { "key": "new-plugin", "discovered": true, "icon": "📦", ... }
  ],
  "installed": ["orm", "auth", "setup"]
}

3.5 — Installation des modules (POST)

// src/app/api/setup/install-modules/route.ts
import { createInstallModulesHandler } from '@mostajs/setup'
import { needsSetup } from '@/lib/setup'

export const { POST } = createInstallModulesHandler(needsSetup)

Body : { "modules": ["orm", "auth", "audit", "rbac"] }

Ce handler :

  1. Resout les dependances transitives (rbacauth + audit)
  2. Installe via npm install (local file:./packages/... si present, sinon npm registry)
  3. Ecrit MOSTAJS_MODULES=orm,auth,audit,rbac dans .env.local

Etape 4 — Creer la page Setup (frontend)

Le module fournit un composant SetupWizard pret a l'emploi avec inline styles (aucune dependance Tailwind/shadcn). Votre page Next.js n'est qu'un wrapper d'une vingtaine de lignes :

// src/app/setup/page.tsx
'use client'

import { useRouter } from 'next/navigation'
import SetupWizard from '@mostajs/setup/components/SetupWizard'
import { t } from '@/i18n'  // ou toute fonction de traduction

export default function SetupPage() {
  const router = useRouter()

  return (
    <SetupWizard
      t={t}
      onComplete={() => router.push('/login')}
      dbNamePrefix="secuaccessdb"
      endpoints={{
        detectModules: '/api/setup/detect-modules',
        testDb: '/api/setup/test-db',
        installModules: '/api/setup/install-modules',
        install: '/api/setup/install',
        uploadJar: '/api/setup/upload-jar',
      }}
    />
  )
}

Props de SetupWizard

Prop Type Default Description
t (key: string) => string (k) => k Fonction de traduction (recoit des cles setup.xxx)
onComplete () => void Callback apres installation reussie (ex: router.push('/login'))
dbNamePrefix string 'mydb' Prefixe pour le nom de base par defaut
persistState boolean true Persister l'etat du wizard dans sessionStorage
endpoints object voir ci-dessous URLs des routes API

Endpoints par defaut :

{
  "detectModules": "/api/setup/detect-modules",
  "testDb": "/api/setup/test-db",
  "installModules": "/api/setup/install-modules",
  "install": "/api/setup/install",
  "uploadJar": "/api/setup/upload-jar"
}

Cles i18n attendues : setup.steps.*, setup.welcome.*, setup.modules.*, setup.dialect.*, setup.database.*, setup.admin.*, setup.summary.*, setup.back, setup.next.

Le wizard inclut :

  • 6 etapes : Accueil → Modules → Dialecte → Base de donnees → Admin → Recapitulatif
  • 13 dialectes avec distinction Premium (grises, non cliquables) et badges JDBC
  • Upload JAR integre pour les dialectes JDBC (hsqldb, oracle, db2, hana, sybase)
  • Persistence sessionStorage pour survivre aux hot-reloads Next.js
  • Resolution des dependances entre modules
  • Retry automatique apres npm install (le serveur peut redemarrer)

Etape 5 — Middleware : rediriger vers /setup

// src/middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(req: NextRequest) {
  const { pathname } = req.nextUrl

  // Laisser passer les routes publiques
  if (
    pathname.startsWith('/setup') ||
    pathname.startsWith('/api/setup') ||
    pathname.startsWith('/api/auth') ||
    pathname.startsWith('/_next')
  ) {
    return NextResponse.next()
  }

  // Verifier si l'app a besoin du setup
  try {
    const res = await fetch(new URL('/api/setup/status', req.url))
    const data = await res.json()
    if (data.needsSetup) {
      return NextResponse.redirect(new URL('/setup', req.url))
    }
  } catch {
    // Si la DB n'est pas accessible, rediriger vers setup
    return NextResponse.redirect(new URL('/setup', req.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/((?!_next|favicon.ico|icons|manifest.json).*)'],
}

Etape 6 — Tester le flux complet

# 1. Demarrer le serveur
npm run dev

# 2. Ouvrir http://localhost:3000
#    → Redirige automatiquement vers /setup

# 3. Suivre le wizard :
#    - Choisir les modules
#    - Selectionner le dialecte (ex: MongoDB, SQLite, Postgres...)
#    - Tester la connexion
#    - Renseigner l'admin
#    - Lancer l'installation

# 4. Verifier les resultats :
#    - .env.local contient DB_DIALECT, SGBD_URI, MOSTAJS_MODULES
#    - La DB contient les roles, permissions et l'admin
#    - /api/setup/status retourne { needsSetup: false }

4. API Reference

Fonctions principales

Export Signature Description
needsSetup (countFn: () => Promise<number>) => Promise<boolean> Retourne true si 0 users en base
runInstall (config: InstallConfig, setup: MostaSetupConfig) => Promise<Result> Flux complet : env → DB → seed → admin
testDbConnection (config: DbTestConfig) => Promise<{ ok, error? }> Test ephemere (ne modifie pas la connexion globale)
composeDbUri (dialect: DialectType, db: DbConfig) => string Compose l'URI de connexion
writeEnvLocal (options: EnvWriterOptions) => Promise<boolean> Ecrit .env.local, retourne true si dialect change

Factories API routes

Export Route Methode Description
createStatusHandler(needsSetup) /api/setup/status GET Retourne { needsSetup: boolean }
createTestDbHandler(needsSetup) /api/setup/test-db POST Teste la connexion DB
createInstallHandler(needsSetup, config) /api/setup/install POST Lance l'installation complete
createDetectModulesHandler() /api/setup/detect-modules GET Liste modules (statiques + npm) + installes
createInstallModulesHandler(needsSetup) /api/setup/install-modules POST Installe les modules npm selectionnes

Data exports

Export Description
DIALECT_INFO Record<DialectType, DialectInfo> — metadata de chaque SGBD
ALL_DIALECTS DialectType[] — liste des 13 dialectes
MODULES ModuleDefinition[] — liste statique des 7 modules connus
resolveModuleDependencies(selected, modules?) Resout les dependances transitives
discoverNpmModules() Decouvre les packages @mostajs/* sur npm

Types

interface InstallConfig {
  dialect: DialectType
  db: DbConfig
  admin: { email: string; password: string; firstName: string; lastName: string }
  seed?: SeedOptions       // { activities: true, demoData: false, ... }
  modules?: string[]       // ['orm', 'auth', 'rbac'] → ecrit MOSTAJS_MODULES
}

interface MostaSetupConfig {
  appName: string
  defaultPort?: number
  seedRBAC?: () => Promise<void>
  createAdmin?: (admin: { email: string; hashedPassword: string; firstName: string; lastName: string }) => Promise<void>
  optionalSeeds?: SeedDefinition[]
  extraEnvVars?: Record<string, string>
}

interface SeedDefinition {
  key: string
  label: string
  description: string
  icon?: string
  default?: boolean
  run: (repos: any) => Promise<void>
}

interface ModuleDefinition {
  key: string
  packageName: string       // '@mostajs/auth'
  localDir?: string         // 'mosta-auth' (sous packages/)
  label: string
  description: string
  icon: string
  required?: boolean
  default?: boolean
  dependsOn?: string[]
  discovered?: boolean      // true si trouve via npm search
}

5. Les 13 dialectes supportes

Dialecte Icone Port Categorie Driver
MongoDB 🍃 27017 document mongoose
SQLite 📁 file better-sqlite3
PostgreSQL 🐘 5432 sql pg
MySQL 🐬 3306 sql mysql2
MariaDB 🦭 3306 sql mariadb
Oracle 🔴 1521 enterprise oracledb
SQL Server 🟦 1433 enterprise tedious
CockroachDB 🪳 26257 distributed pg
IBM DB2 🏢 50000 enterprise ibm_db
SAP HANA 💎 39013 enterprise @sap/hana-client
HyperSQL 9001 legacy
Cloud Spanner ☁️ distributed @google-cloud/spanner
Sybase ASE 🔷 5000 legacy sybase

Les URI sont composees automatiquement par composeDbUri() :

mongodb://user:pass@host:27017/mydb
postgresql://user:pass@host:5432/mydb
mysql://user:pass@host:3306/mydb
./data/mydb.db                           (SQLite)
spanner://projects/my-project            (Cloud Spanner)

6. Systeme de modules

Catalogue des 7 modules connus

Module Package Requis Depend de Standalone Description
orm @mostajs/orm oui ORM multi-dialecte (13 SGBD), pattern Hibernate
auth @mostajs/auth oui orm non NextAuth, sessions, hashage mots de passe
audit @mostajs/audit non orm non Journalisation des actions, tracabilite
rbac @mostajs/rbac non auth, audit non Roles, permissions, matrice RBAC
settings @mostajs/settings non orm non Parametres cle-valeur, formulaire auto, provider React
face @mostajs/face non aucune oui Detection de visage, descripteurs, matching 1:N
setup @mostajs/setup oui orm non Wizard d'installation, test DB, seed runner

Module standalone : @mostajs/face

@mostajs/face est 100% independant — il n'importe aucun package @mostajs/* et peut etre utilise dans n'importe quelle application React >= 18 sans @mostajs/orm ni base de donnees.

Dependance unique : @vladmandic/face-api (reconnaissance faciale TensorFlow.js)

npm install @mostajs/face
import { useCamera, useFaceDetection, compareFaces } from '@mostajs/face'

// Hooks React pour camera et detection
const { videoRef, start, stop } = useCamera()
const { detect, result } = useFaceDetection()

// API bas niveau
import { loadModels, detectFace, extractDescriptor } from '@mostajs/face'
import { findMatch, descriptorToArray, arrayToDescriptor } from '@mostajs/face'

Exports : loadModels, detectFace, detectAllFaces, extractDescriptor, compareFaces, findMatch, findAllMatches, descriptorToArray, arrayToDescriptor, isValidDescriptor, drawDetection, useCamera, useFaceDetection.

Graphe de dependances

                    ┌──────────┐
                    │ orm (R)  │  R = requis
                    └────┬─────┘
                ┌────────┼────────┬──────────┐
                v        v        v          v
          ┌──────────┐ ┌──────┐ ┌──────────┐ ┌───────┐
          │ auth (R) │ │audit │ │ settings │ │setup(R)│
          └────┬─────┘ └──┬───┘ └──────────┘ └───────┘
               │          │
               v          v
          ┌──────────────────┐
          │       rbac       │
          └──────────────────┘

  ┌──────────────────────────┐
  │   face (100% standalone) │  ← aucune dependance @mostajs
  └──────────────────────────┘

Liste statique vs decouverte dynamique

Le package maintient une liste statique des 7 modules ci-dessus avec metadata riches (required, dependsOn, icon, description).

Au runtime, GET /api/setup/detect-modules interroge aussi npm (npm search @mostajs --json) pour trouver des packages publies apres le deploiement. Ces modules decouverts sont ajoutes avec discovered: true et l'icone 📦.

Resolution des dependances

import { resolveModuleDependencies } from '@mostajs/setup'

resolveModuleDependencies(['rbac'])
// → ['rbac', 'auth', 'audit', 'orm', 'setup']
// (rbac depend de auth + audit, auth depend de orm, setup est requis)

resolveModuleDependencies(['face'])
// → ['face', 'orm', 'auth', 'setup']
// (face n'a pas de dependance @mostajs, mais orm/auth/setup sont requis)

Installation hybride (local + npm)

Le handler install-modules utilise une strategie hybride :

  1. Skip : si deja dans node_modules/@mostajs/xxx → pas de npm install (evite hot-reload)
  2. Local : si packages/mosta-xxx/ existe → npm install file:./packages/mosta-xxx
  3. npm registry : sinon → npm install @mostajs/xxx

Cela evite les 404 npm pour les packages non encore publies et les hot-reloads Next.js inutiles.


7. Exemples avances

Utiliser runInstall sans le wizard UI

import { runInstall } from '@mostajs/setup'
import { setupConfig } from './my-setup-config'

const result = await runInstall(
  {
    dialect: 'postgres',
    db: { host: 'localhost', port: 5432, name: 'mydb', user: 'pg', password: 'secret' },
    admin: { email: 'admin@example.com', password: 'Admin@123', firstName: 'Admin', lastName: 'User' },
    seed: { demoData: true },
    modules: ['orm', 'auth', 'rbac'],
  },
  setupConfig,
)

console.log(result)
// { ok: true, needsRestart: false, seeded: ['categories', 'permissions', 'roles', 'admin', 'demoData'] }

Ajouter des variables d'environnement personnalisees

const config: MostaSetupConfig = {
  appName: 'MyApp',
  extraEnvVars: {
    SMTP_HOST: 'smtp.example.com',
    STRIPE_KEY: 'sk_test_...',
  },
}
// → .env.local contiendra aussi SMTP_HOST et STRIPE_KEY

Tester la connexion DB programmatiquement

import { testDbConnection } from '@mostajs/setup'

const result = await testDbConnection({
  dialect: 'mongodb',
  host: 'localhost',
  port: 27017,
  name: 'testdb',
  user: '',
  password: '',
})
// { ok: true } ou { ok: false, error: 'Connection refused' }

Composer une URI manuellement

import { composeDbUri } from '@mostajs/setup'

composeDbUri('postgres', { host: 'db.example.com', port: 5432, name: 'prod', user: 'app', password: 's3cret' })
// → 'postgresql://app:s3cret@db.example.com:5432/prod'

composeDbUri('sqlite', { host: '', port: 0, name: 'myapp', user: '', password: '' })
// → './data/myapp.db'

Ecrire .env.local directement

import { writeEnvLocal } from '@mostajs/setup'

const dialectChanged = await writeEnvLocal({
  dialect: 'mongodb',
  uri: 'mongodb://localhost:27017/mydb',
  port: 3000,
  extraVars: { MOSTAJS_MODULES: 'orm,auth,rbac' },
})
// dialectChanged = true si le dialecte a change (necessite un redemarrage)

8. Reconfiguration (post-installation)

Apres l'installation initiale, le module fournit un panneau de reconfiguration permettant de :

  • Changer de base de donnees (dialecte, connexion, test)
  • Activer / desactiver des modules @mostajs
  • Optionnellement re-seeder la nouvelle base

Integration dans le projet hote

Ce module exporte un composant React (ReconfigPanel) et une factory API (createReconfigHandlers), mais ne cree pas de pages Next.js. Le projet hote doit creer la route API et la page.

1. Route API

src/app/api/setup/reconfig/route.ts

import { createReconfigHandlers } from '@mostajs/setup/api/reconfig'

const { GET, POST } = createReconfigHandlers()
export { GET, POST }

2. Route API JAR Upload (drivers JDBC)

src/app/api/setup/upload-jar/route.ts

// Author: Dr Hamid MADANI drmdh@msn.com
import { createUploadJarHandlers } from '@mostajs/setup/api/upload-jar'

const { GET, POST, DELETE } = createUploadJarHandlers()
export { GET, POST, DELETE }

La logique d'upload est dans @mostajs/orm (saveJarFile, deleteJarFile, listJarFiles). La route factory dans @mostajs/setup ne fait que la deleguer.

  • GET — liste les JARs et le statut des dialects JDBC
  • POST — upload un fichier .jar (multipart/form-data, champ jar)
  • DELETE — supprime un JAR ({ "fileName": "hsqldb-2.7.2.jar" })

3. Page de reconfiguration

src/app/dashboard/settings/reconfig/page.tsx

'use client'
import ReconfigPanel from '@mostajs/setup/components/ReconfigPanel'

export default function ReconfigPage() {
  return (
    <div className="space-y-6">
      <h1 className="text-2xl font-bold">Reconfiguration</h1>
      <ReconfigPanel
        apiEndpoint="/api/setup/reconfig"
        detectEndpoint="/api/setup/detect-modules"
        jarEndpoint="/api/setup/upload-jar"
        showSeedOption
        onDbChanged={() => window.location.reload()}
        onSeedRequested={async () => {
          await fetch('/api/setup/install', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ action: 'seed-only' }),
          })
        }}
      />
    </div>
  )
}

3. Menu dynamique

Le module exporte setupMenuContribution qui declare la route /dashboard/settings/reconfig dans le groupe "Administration". Importez-le via le deep import :

import { setupMenuContribution } from '@mostajs/setup/lib/menu'

4. Props de ReconfigPanel

Prop Type Description
apiEndpoint string URL de l'API reconfig (ex: /api/setup/reconfig)
detectEndpoint string URL de l'API detect-modules (ex: /api/setup/detect-modules)
t (key: string) => string Fonction de traduction (optionnel)
showSeedOption boolean Afficher la checkbox "Re-seeder" lors d'un changement de DB
onDbChanged () => void Callback apres changement de DB reussi
onModulesChanged (modules: string[]) => void Callback apres maj des modules
onSeedRequested () => Promise<void> Callback pour executer le seed

5. Pourquoi le projet hote doit creer les pages ?

Les modules @mostajs/* sont des bibliotheques npm (composants, hooks, utilitaires), pas des applications. Next.js App Router exige que les fichiers page.tsx soient dans le dossier src/app/ du projet. Un package npm ne peut pas injecter de pages dans le routeur — c'est donc au projet hote de creer ces fichiers wrapper, meme s'ils ne font qu'importer et afficher un composant du module.


9. FAQ / Troubleshooting

L'installation tourne en boucle (GET /setup se repete)

Cause : npm install modifie package.json / node_modules, ce qui declenche un hot-reload Next.js et reinitialise le state React.

Solution : Le handler install-modules skip les packages deja installes dans node_modules/. Si le probleme persiste, ajoutez une persistence du state wizard dans sessionStorage (voir l'implementation de SecuAccess Pro).

E11000 duplicate key error (MongoDB)

Cause : Un champ unique: true sans required: true dans le schema. MongoDB indexe les null et refuse le doublon.

Solution : Ajoutez sparse: true au champ dans votre EntitySchema. Cela fonctionne avec MongoDB (index sparse) et est ignore sans risque par les dialectes SQL.

clientNumber: { type: 'string', unique: true, sparse: true }

JSON.parse: unexpected character at line 1 column 1

Cause : La reponse de l'API est du HTML (page 404 Next.js) au lieu de JSON, typiquement pendant un hot-reload du serveur.

Solution : Utilisez un safeJson() helper dans le frontend :

async function safeJson(res: Response) {
  try { return JSON.parse(await res.text()) }
  catch { return null }
}

npm search timeout (pas d'internet)

Le handler detect-modules a un timeout de 10 secondes sur npm search. En cas d'echec (offline, timeout), il retourne la liste statique des 7 modules connus. Aucune action necessaire.

Comment ajouter un nouveau module a la liste statique ?

Editez packages/mosta-setup/data/module-definitions.ts et ajoutez une entree a MODULES :

{
  key: 'notifications',
  packageName: '@mostajs/notifications',
  localDir: 'mosta-notifications',     // si disponible localement
  label: 'Notifications',
  description: 'Push, email, SMS',
  icon: '🔔',
  default: false,
  dependsOn: ['orm'],
}

Puis recompilez : cd packages/mosta-setup && npx tsc


Related Packages

Package Depend de orm Standalone Description
@mostajs/orm Multi-dialect ORM, 13 SGBD (requis)
@mostajs/auth oui non Authentication NextAuth, sessions
@mostajs/audit oui non Audit logging, tracabilite
@mostajs/rbac oui non Roles & Permissions RBAC
@mostajs/settings oui non Parametres cle-valeur
@mostajs/face non oui Reconnaissance faciale (independant)

License

MIT — (c) 2025 Dr Hamid MADANI drmdh@msn.com

About

Reusable setup wizard module — multi-dialect DB configuration, .env.local writer, seed runner

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors