Add module management system for enabling/disabling features

Stores can enable/disable feature modules from Settings. When disabled,
nav links are hidden and API routes return 403. Designed as the
foundation for future license-based gating (licensed + enabled flags).

Core modules (Accounts, Members, Users, Roles, Settings) are always on.

- module_config table with slug, name, description, licensed, enabled
- In-memory cache for fast per-request module checks
- requireModule middleware wraps route groups in main.ts
- Settings page Modules card with toggle switches
- Sidebar hides nav links for disabled modules
- Default modules seeded: inventory, pos, repairs, rentals, lessons,
  files, vault, email, reports
This commit is contained in:
Ryan Moon
2026-03-30 06:52:27 -05:00
parent 1f9297f533
commit e346e072b8
10 changed files with 294 additions and 13 deletions

View File

@@ -0,0 +1,26 @@
import type { FastifyPluginAsync } from 'fastify'
import { ModuleService } from '../../services/module.service.js'
export const moduleRoutes: FastifyPluginAsync = async (app) => {
app.get('/modules', { preHandler: [app.authenticate, app.requirePermission('settings.view')] }, async (_request, reply) => {
const modules = await ModuleService.listAll(app.db)
return reply.send({ data: modules })
})
app.patch('/modules/:slug', { preHandler: [app.authenticate, app.requirePermission('settings.edit')] }, async (request, reply) => {
const { slug } = request.params as { slug: string }
const { enabled } = request.body as { enabled?: boolean }
if (typeof enabled !== 'boolean') {
return reply.status(400).send({ error: { message: 'enabled (boolean) is required', statusCode: 400 } })
}
try {
const updated = await ModuleService.setEnabled(app.db, slug, enabled)
if (!updated) return reply.status(404).send({ error: { message: 'Module not found', statusCode: 404 } })
return reply.send(updated)
} catch (err: any) {
return reply.status(400).send({ error: { message: err.message, statusCode: 400 } })
}
})
}