feat(settings): implement tenant-scoped category setting endpoints for General, Notifications, Security, API Keys
This commit is contained in:
@@ -45,6 +45,24 @@ export class SettingController {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getByCategory(req, res, next) {
|
||||
try {
|
||||
const data = await service.getCategorySettings(req.params.category, req.user);
|
||||
return res.status(200).json({ success: true, data });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async updateByCategory(req, res, next) {
|
||||
try {
|
||||
const data = await service.upsertCategorySettings(req.params.category, req.body, req.user);
|
||||
return res.status(200).json({ success: true, message: 'Settings saved successfully', data });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new SettingController();
|
||||
|
||||
@@ -14,6 +14,23 @@ export default (sequelize) => {
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
tenant_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
category: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
defaultValue: 'general'
|
||||
},
|
||||
key: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: true
|
||||
},
|
||||
value: {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
|
||||
@@ -29,6 +29,18 @@ router.get(
|
||||
controller.getAll
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/by-category/:category',
|
||||
authenticate,
|
||||
controller.getByCategory
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/by-category/:category',
|
||||
authenticate,
|
||||
controller.updateByCategory
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/v1/settings/{id}:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import repository from './setting.repository.js';
|
||||
import { models } from '../../../shared/database/models.js';
|
||||
import { SocketService } from '../../../shared/services/socket.service.js';
|
||||
import { AuditService } from '../../../shared/services/audit.service.js';
|
||||
import { ApiError } from '../../../utils/helpers/ApiError.utils.js';
|
||||
@@ -54,6 +55,57 @@ export class SettingService {
|
||||
return record;
|
||||
}
|
||||
|
||||
async getCategorySettings(category, userContext = {}) {
|
||||
const tenantId = userContext.tenant_id || userContext.tenantId || null;
|
||||
const where = { category };
|
||||
if (tenantId) {
|
||||
where.tenant_id = tenantId;
|
||||
}
|
||||
|
||||
const settings = await models.Setting.findAll({ where });
|
||||
const result = {};
|
||||
for (const setting of settings) {
|
||||
if (setting.key) {
|
||||
result[setting.key] = setting.value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async upsertCategorySettings(category, settingsData = {}, userContext = {}) {
|
||||
const tenantId = userContext.tenant_id || userContext.tenantId || null;
|
||||
|
||||
for (const [key, value] of Object.entries(settingsData)) {
|
||||
const where = { category, key };
|
||||
if (tenantId) {
|
||||
where.tenant_id = tenantId;
|
||||
}
|
||||
|
||||
const existing = await models.Setting.findOne({ where });
|
||||
if (existing) {
|
||||
await existing.update({ value });
|
||||
} else {
|
||||
await models.Setting.create({
|
||||
tenant_id: tenantId,
|
||||
category,
|
||||
key,
|
||||
value,
|
||||
name: `${category}.${key}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Audit Log
|
||||
await AuditService.log({
|
||||
action: 'UPDATE_SETTINGS',
|
||||
resource: 'Setting',
|
||||
userId: userContext.user_id || 'system',
|
||||
details: { category, settingsData }
|
||||
});
|
||||
|
||||
return await this.getCategorySettings(category, userContext);
|
||||
}
|
||||
|
||||
async delete(id, userContext = {}) {
|
||||
const deleted = await repository.delete(id);
|
||||
if (!deleted) {
|
||||
|
||||
Reference in New Issue
Block a user