File size: 3,038 Bytes
839aef2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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;