Spaces:
Sleeping
Sleeping
| import { randomUUID } from 'crypto'; | |
| import { DataTypes } from 'sequelize'; | |
| import { sequelize } from '../db.js'; | |
| import { enumField, CommandType, CommandStatus } from '../utils/enums.js'; | |
| /** | |
| * Build a JSONB-compatible attribute. The old Python backend's JSONB type stored | |
| * JSON as TEXT on SQLite and native JSON/JSONB on Postgres/MySQL. We mirror that: | |
| * always (de)serialize through getters/setters so a TEXT column round-trips, and | |
| * native JSON columns also work (object passes through). | |
| */ | |
| function jsonbField(field, defaultValue) { | |
| return { | |
| type: DataTypes.TEXT, | |
| field, | |
| defaultValue: defaultValue === undefined ? undefined : JSON.stringify(defaultValue), | |
| get() { | |
| const raw = this.getDataValue(field); | |
| if (raw === null || raw === undefined) return raw; | |
| if (typeof raw === 'object') return raw; // native JSON column | |
| try { | |
| return JSON.parse(raw); | |
| } catch { | |
| return raw; | |
| } | |
| }, | |
| set(val) { | |
| if (val === null || val === undefined) { | |
| this.setDataValue(field, val); | |
| } else if (typeof val === 'string') { | |
| this.setDataValue(field, val); | |
| } else { | |
| this.setDataValue(field, JSON.stringify(val)); | |
| } | |
| }, | |
| }; | |
| } | |
| export const DeviceCommand = sequelize.define( | |
| 'DeviceCommand', | |
| { | |
| id: { type: DataTypes.STRING(36), primaryKey: true, defaultValue: randomUUID }, | |
| device_id: { type: DataTypes.STRING(36), allowNull: false }, | |
| command_type: enumField({ field: 'command_type', allowNull: false }), | |
| payload: jsonbField('payload', {}), | |
| status: enumField({ field: 'status', defaultValue: 'pending' }), | |
| retry_count: { type: DataTypes.STRING(10), defaultValue: '0' }, | |
| max_retries: { type: DataTypes.STRING(10), defaultValue: '3' }, | |
| fcm_message_id: { type: DataTypes.STRING(255) }, | |
| created_at: { type: DataTypes.DATE, defaultValue: DataTypes.NOW }, | |
| sent_at: { type: DataTypes.DATE }, | |
| delivered_at: { type: DataTypes.DATE }, | |
| executed_at: { type: DataTypes.DATE }, | |
| error_message: { type: DataTypes.TEXT }, | |
| issued_by: { type: DataTypes.STRING(36) }, | |
| reason: { type: DataTypes.TEXT }, | |
| }, | |
| { tableName: 'device_commands', timestamps: false }, | |
| ); | |
| export const AuditLog = sequelize.define( | |
| 'AuditLog', | |
| { | |
| id: { type: DataTypes.STRING(36), primaryKey: true, defaultValue: randomUUID }, | |
| entity_type: { type: DataTypes.STRING(50), allowNull: false }, | |
| entity_id: { type: DataTypes.STRING(36) }, | |
| action: { type: DataTypes.STRING(100), allowNull: false }, | |
| old_value: jsonbField('old_value'), | |
| new_value: jsonbField('new_value'), | |
| performed_by: { type: DataTypes.STRING(36) }, | |
| ip_address: { type: DataTypes.STRING(50) }, | |
| user_agent: { type: DataTypes.TEXT }, | |
| created_at: { type: DataTypes.DATE, defaultValue: DataTypes.NOW }, | |
| }, | |
| { tableName: 'audit_logs', timestamps: false }, | |
| ); | |
| export { CommandType, CommandStatus }; | |
| export default DeviceCommand; | |