feat(projects): gate Project Overview behind a projects feature flag + cockpit email actions
- Migration 120 seeds the projects flag (default OFF), idempotent. - Backend feature-flags whitelist + DEFAULT_FLAGS + clients derivation. - adminProjects routes 403 PROJECTS_DISABLED when the flag is off. - projectService email actions (resend/cancel/retry/send-now) + routes. - Frontend flag type, DEFAULT_FLAGS, Features tab card (en+de).
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Migration: seed the `projects` feature flag (default OFF).
|
||||
*
|
||||
* Gates the admin-only Project Overview cockpit + the "book to project" hours
|
||||
* control. Off by default so existing installs don't suddenly surface a new
|
||||
* top-level CRM area — the admin opts in under Settings → Features, exactly
|
||||
* like bills/quotes/contracts/hours.
|
||||
*
|
||||
* Idempotent: inserts only when the row is missing (migration 088 already
|
||||
* seeded the original flag set on fresh installs and won't re-run).
|
||||
*/
|
||||
|
||||
exports.up = async function (knex) {
|
||||
if (!(await knex.schema.hasTable('feature_flags'))) return;
|
||||
const existing = await knex('feature_flags').where({ key: 'projects' }).first();
|
||||
if (!existing) {
|
||||
await knex('feature_flags').insert({ key: 'projects', value: false });
|
||||
}
|
||||
};
|
||||
|
||||
exports.down = async function (knex) {
|
||||
if (!(await knex.schema.hasTable('feature_flags'))) return;
|
||||
await knex('feature_flags').where({ key: 'projects' }).del();
|
||||
};
|
||||
Reference in New Issue
Block a user