You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

321 lines
7.5 KiB

2 years ago
  1. package mysqldb
  2. import (
  3. "context"
  4. "database/sql"
  5. "encoding/json"
  6. "fmt"
  7. lucifer3 "git.aiterp.net/lucifer3/server"
  8. "git.aiterp.net/lucifer3/server/commands"
  9. "git.aiterp.net/lucifer3/server/effects"
  10. "git.aiterp.net/lucifer3/server/events"
  11. "git.aiterp.net/lucifer3/server/internal/gentools"
  12. "git.aiterp.net/lucifer3/server/services/mysqldb/mysqlgen"
  13. "time"
  14. _ "github.com/go-sql-driver/mysql"
  15. )
  16. type database struct {
  17. db *sql.DB
  18. }
  19. func (d *database) Active() bool {
  20. return true
  21. }
  22. func (d *database) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
  23. q := mysqlgen.New(d.db)
  24. timeout, cancel := context.WithTimeout(context.Background(), time.Second/2)
  25. defer cancel()
  26. switch event := event.(type) {
  27. case events.Started:
  28. timeout, cancel := context.WithTimeout(context.Background(), time.Second*10)
  29. defer cancel()
  30. // Fetch all aliases first
  31. aliases, err := q.ListDeviceAliases(timeout)
  32. if err != nil {
  33. bus.RunEvent(events.Log{
  34. Level: "error",
  35. Code: "database_could_not_list_aliases",
  36. Message: "Database could not list aliases: " + err.Error(),
  37. })
  38. }
  39. auths, err := q.ListDeviceAuth(timeout)
  40. if err != nil {
  41. bus.RunEvent(events.Log{
  42. Level: "error",
  43. Code: "database_could_not_list_device_infos",
  44. Message: "Hardware State serialization failed: " + err.Error(),
  45. })
  46. }
  47. for _, auth := range auths {
  48. bus.RunCommand(commands.ConnectDevice{
  49. ID: auth.ID,
  50. APIKey: auth.ApiKey,
  51. })
  52. }
  53. infos, err := q.ListDeviceInfos(timeout)
  54. if err != nil {
  55. bus.RunEvent(events.Log{
  56. Level: "error",
  57. Code: "database_could_not_list_device_infos",
  58. Message: "Hardware State serialization failed: " + err.Error(),
  59. })
  60. }
  61. for _, info := range infos {
  62. switch info.Kind {
  63. case "hardware_state":
  64. staleEvent := events.HardwareState{}
  65. err := json.Unmarshal(info.Data, &staleEvent)
  66. if err == nil {
  67. bus.RunEvent(staleEvent)
  68. }
  69. case "hardware_metadata":
  70. staleEvent := events.HardwareMetadata{}
  71. err := json.Unmarshal(info.Data, &staleEvent)
  72. if err == nil {
  73. bus.RunEvent(staleEvent)
  74. }
  75. }
  76. }
  77. // Run the aliases now
  78. for _, alias := range aliases {
  79. bus.RunCommand(commands.AddAlias{
  80. Match: alias.ID,
  81. Alias: alias.Alias,
  82. })
  83. }
  84. assignments, err := q.ListDeviceAssignments(timeout)
  85. if err != nil {
  86. bus.RunEvent(events.Log{
  87. Level: "error",
  88. Code: "database_could_not_list_assignments",
  89. Message: "Database could not list assignments: " + err.Error(),
  90. })
  91. }
  92. for _, ass := range assignments {
  93. effect := effects.Serializable{}
  94. err := json.Unmarshal(ass.Effect, &effect)
  95. if err != nil {
  96. bus.RunEvent(events.Log{
  97. Level: "error",
  98. Code: "database_could_not_deserialize_effect",
  99. Message: err.Error(),
  100. })
  101. continue
  102. }
  103. bus.RunCommand(commands.Assign{
  104. ID: gentools.ShallowCopy(&ass.ID),
  105. Match: ass.Match,
  106. Effect: effect.Effect,
  107. })
  108. }
  109. case events.DeviceAccepted:
  110. if event.Extras == nil {
  111. event.Extras = map[string]string{}
  112. }
  113. extras, err := json.Marshal(event.Extras)
  114. if err != nil {
  115. extras = []byte("{}")
  116. }
  117. err = q.ReplaceDeviceAuth(timeout, mysqlgen.ReplaceDeviceAuthParams{
  118. ID: event.ID,
  119. ApiKey: event.APIKey,
  120. Extras: extras,
  121. })
  122. if err != nil {
  123. bus.RunEvent(events.Log{
  124. Level: "error",
  125. Code: "database_could_not_save_device_auth",
  126. Message: "Device auth save failed: " + err.Error(),
  127. })
  128. }
  129. case events.HardwareState:
  130. if event.Stale {
  131. return
  132. }
  133. event.Stale = true
  134. serialized, err := json.Marshal(event)
  135. if err != nil {
  136. bus.RunEvent(events.Log{
  137. Level: "error",
  138. Code: "database_could_not_serialize_hardware_state",
  139. Message: "Hardware State serialization failed: " + err.Error(),
  140. })
  141. return
  142. }
  143. err = q.ReplaceDeviceInfo(timeout, mysqlgen.ReplaceDeviceInfoParams{
  144. ID: event.ID,
  145. Kind: "hardware_state",
  146. Data: serialized,
  147. })
  148. if err != nil {
  149. bus.RunEvent(events.Log{
  150. Level: "error",
  151. Code: "database_could_not_save_hardware_state",
  152. Message: err.Error(),
  153. })
  154. return
  155. }
  156. case events.HardwareMetadata:
  157. if event.Stale {
  158. return
  159. }
  160. event.Stale = true
  161. serialized, err := json.Marshal(event)
  162. if err != nil {
  163. bus.RunEvent(events.Log{
  164. Level: "error",
  165. Code: "database_could_not_serialize_hardware_state",
  166. Message: "Hardware State serialization failed: " + err.Error(),
  167. })
  168. return
  169. }
  170. err = q.ReplaceDeviceInfo(timeout, mysqlgen.ReplaceDeviceInfoParams{
  171. ID: event.ID,
  172. Kind: "hardware_metadata",
  173. Data: serialized,
  174. })
  175. if err != nil {
  176. bus.RunEvent(events.Log{
  177. Level: "error",
  178. Code: "database_could_not_save_hardware_state",
  179. Message: err.Error(),
  180. })
  181. return
  182. }
  183. case events.AssignmentCreated:
  184. serialized, err := json.Marshal(&effects.Serializable{Effect: event.Effect})
  185. if err != nil {
  186. bus.RunEvent(events.Log{
  187. Level: "error",
  188. Code: "database_could_not_serialize_effect",
  189. Message: err.Error(),
  190. })
  191. return
  192. }
  193. err = q.ReplaceDeviceAssignment(timeout, mysqlgen.ReplaceDeviceAssignmentParams{
  194. ID: event.ID,
  195. CreatedDate: time.Now().UTC(),
  196. Match: event.Match,
  197. Effect: serialized,
  198. })
  199. if err != nil {
  200. bus.RunEvent(events.Log{
  201. Level: "error",
  202. Code: "database_could_not_save_assignment",
  203. Message: err.Error(),
  204. })
  205. return
  206. }
  207. case events.AssignmentRemoved:
  208. err := q.DeleteDeviceAssignment(timeout, event.ID)
  209. if err != nil {
  210. bus.RunEvent(events.Log{
  211. Level: "error",
  212. Code: "database_could_not_remove_assignment",
  213. Message: err.Error(),
  214. })
  215. return
  216. }
  217. case events.AliasAdded:
  218. err := q.InsertDeviceAlias(timeout, mysqlgen.InsertDeviceAliasParams{
  219. ID: event.ID,
  220. Alias: event.Alias,
  221. })
  222. if err != nil {
  223. bus.RunEvent(events.Log{
  224. Level: "error",
  225. Code: "database_could_not_remove_assignment",
  226. Message: err.Error(),
  227. })
  228. return
  229. }
  230. case events.AliasRemoved:
  231. err := q.DeleteDeviceAlias(timeout, mysqlgen.DeleteDeviceAliasParams{
  232. ID: event.ID,
  233. Alias: event.Alias,
  234. })
  235. if err != nil {
  236. bus.RunEvent(events.Log{
  237. Level: "error",
  238. Code: "database_could_not_remove_assignment",
  239. Message: err.Error(),
  240. })
  241. return
  242. }
  243. case events.DeviceForgotten:
  244. err := q.DeleteDeviceInfoByID(timeout, event.ID)
  245. if err != nil {
  246. bus.RunEvent(events.Log{
  247. Level: "error",
  248. Code: "database_could_not_remove_info",
  249. Message: "Failed to remove device info: " + err.Error(),
  250. })
  251. }
  252. _ = q.DeleteDeviceAuth(timeout, event.ID)
  253. err = q.DeleteDeviceAliasByID(timeout, event.ID)
  254. if err != nil {
  255. bus.RunEvent(events.Log{
  256. Level: "error",
  257. Code: "database_could_not_remove_aliases",
  258. Message: "Failed to remove device aliases: " + err.Error(),
  259. })
  260. }
  261. err = q.DeleteDeviceAliasByIDLike(timeout, event.ID+":%")
  262. if err != nil {
  263. bus.RunEvent(events.Log{
  264. Level: "error",
  265. Code: "database_could_not_remove_sub_aliases",
  266. Message: "Failed to remove sub-device aliases: " + err.Error(),
  267. })
  268. }
  269. }
  270. }
  271. func (d *database) HandleCommand(*lucifer3.EventBus, lucifer3.Command) {}
  272. func Connect(host string, port int, username, password, dbname string) (lucifer3.ActiveService, error) {
  273. db, err := sql.Open("mysql", fmt.Sprintf(
  274. "%s:%s@(%s:%d)/%s?parseTime=true", username, password, host, port, dbname,
  275. ))
  276. if err != nil {
  277. return nil, err
  278. }
  279. db.SetMaxOpenConns(10)
  280. db.SetMaxIdleConns(10)
  281. db.SetConnMaxIdleTime(time.Minute)
  282. err = db.Ping()
  283. if err != nil {
  284. return nil, err
  285. }
  286. return &database{db: db}, nil
  287. }