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.

332 lines
9.3 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. const express = require("express");
  2. const Workout = require("../systems/workout");
  3. /**
  4. * @param {import("../repositories/sqlite3")} repo
  5. */
  6. module.exports = function workoutRouter(repo) {
  7. const router = express.Router({caseSensitive: false});
  8. /** @type {Workout[]} */
  9. let workouts = [];
  10. function activeData(id) {
  11. const workout = workouts.find(w2 => w2.id === id);
  12. if (workout == null) {
  13. return {
  14. active: false,
  15. };
  16. }
  17. return {
  18. active: true,
  19. state: workout.state,
  20. }
  21. }
  22. router.get("/", async(req, res) => {
  23. if (req.query.active === "true") {
  24. return res.status(200).json(workouts.map(w => ({
  25. id: w.id,
  26. bike: w.bike,
  27. program: w.program,
  28. date: w.date,
  29. ...activeData(w.id),
  30. })));
  31. }
  32. try {
  33. const dbWorkouts = await repo.listWorkouts();
  34. return res.status(200).json(dbWorkouts.map(w => ({
  35. ...w,
  36. ...activeData(w.id),
  37. })));
  38. } catch (err) {
  39. return res.status(400).json({code: 400, message: err.message || err})
  40. }
  41. });
  42. router.get("/:id", async(req, res) => {
  43. let workout = workouts.find(w => w.id == req.params.id);
  44. if (workout != null) {
  45. return res.status(200).json({
  46. id: workout.id,
  47. bike: workout.bike,
  48. program: workout.program,
  49. date: workout.date,
  50. cooldownMin: workout.cooldownMin,
  51. ...activeData(workout.id),
  52. });
  53. }
  54. try {
  55. workout = await repo.findWorkout(req.params.id);
  56. if (workout == null) {
  57. return res.status(404).json({code: 404, message: "workout not found"})
  58. }
  59. return res.status(200).json({active: false, ...workout});
  60. } catch (err) {
  61. return res.status(500).json({code: 500, message: err.message || err})
  62. }
  63. });
  64. router.get("/:id/measurements/", async(req, res) => {
  65. try {
  66. const measurements = await repo.listMeasurements(req.params.id);
  67. if (measurements == null) {
  68. return res.status(404).json({code: 404, message: "measurements not found"})
  69. }
  70. for (const measurement of measurements) {
  71. delete measurement.id;
  72. delete measurement.workoutId;
  73. }
  74. return res.status(200).json(measurements);
  75. } catch (err) {
  76. return res.status(500).json({code: 500, message: err.message || err})
  77. }
  78. });
  79. router.post("/", async(req, res) => {
  80. try {
  81. const {bikeId, programId} = req.body;
  82. const workout = await Workout.create(repo, bikeId, programId);
  83. workouts.push(workout);
  84. return res.status(200).json({
  85. id: workout.id,
  86. bike: workout.bike,
  87. program: workout.program,
  88. date: workout.date,
  89. cooldownMin: workout.cooldownMin,
  90. ...activeData(workout.id),
  91. });
  92. } catch (err) {
  93. return res.status(400).json({code: 400, message: err.message || err});
  94. }
  95. });
  96. router.put("/:id", async(req, res) => {
  97. let workout = workouts.find(w => w.id == req.params.id);
  98. if (workout == null) {
  99. return res.status(404).json({code: 404, message: "Workout not found or inactive."})
  100. }
  101. try {
  102. if (req.body.cooldownMin) {
  103. workout.setCooldownMin(req.body.cooldownMin);
  104. await repo.updateWorkout(workout)
  105. }
  106. return res.status(200).json({
  107. id: workout.id,
  108. bike: workout.bike,
  109. program: workout.program,
  110. date: workout.date,
  111. cooldownMin: workout.cooldownMin,
  112. ...activeData(workout.id),
  113. });
  114. } catch (err) {
  115. return res.status(400).json({code: 400, message: err.message || err})
  116. }
  117. });
  118. router.post("/:id/continue", async(req, res) => {
  119. let workout = workouts.find(w => w.id == req.params.id);
  120. if (workout != null) {
  121. return res.status(400).json({code: 400, message: "Workout already active."})
  122. }
  123. try {
  124. workout = await Workout.continue(repo, req.params.id);
  125. workouts.push(workout);
  126. return res.status(200).json({
  127. id: workout.id,
  128. bike: workout.bike,
  129. program: workout.program,
  130. date: workout.date,
  131. cooldownMin: workout.cooldownMin,
  132. ...activeData(workout.id),
  133. });
  134. } catch (err) {
  135. return res.status(400).json({code: 400, message: err.message || err})
  136. }
  137. });
  138. router.post("/:id/connect", async(req, res) => {
  139. const workout = workouts.find(w => w.id == req.params.id);
  140. if (workout == null) {
  141. return res.status(404).json({code: 404, message: "Workout not found or inactive."})
  142. }
  143. try {
  144. await workout.connect();
  145. return res.status(200).json({
  146. id: workout.id,
  147. bike: workout.bike,
  148. program: workout.program,
  149. date: workout.date,
  150. cooldownMin: workout.cooldownMin,
  151. ...activeData(workout.id),
  152. });
  153. } catch(err) {
  154. return res.status(500).json({code: 500, message: err.message || err});
  155. }
  156. });
  157. router.post("/:id/start", async(req, res) => {
  158. const workout = workouts.find(w => w.id == req.params.id);
  159. if (workout == null) {
  160. return res.status(404).json({code: 404, message: "Workout not found or inactive."})
  161. }
  162. try {
  163. await workout.start();
  164. return res.status(200).json({
  165. id: workout.id,
  166. bike: workout.bike,
  167. program: workout.program,
  168. date: workout.date,
  169. cooldownMin: workout.cooldownMin,
  170. ...activeData(workout.id),
  171. });
  172. } catch(err) {
  173. return res.status(500).json({code: 500, message: err.message || err});
  174. }
  175. });
  176. router.post("/:id/pause", async(req, res) => {
  177. const workout = workouts.find(w => w.id == req.params.id);
  178. if (workout == null) {
  179. return res.status(404).json({code: 404, message: "Workout not found or inactive."})
  180. }
  181. try {
  182. await workout.pause();
  183. return res.status(200).json({
  184. id: workout.id,
  185. bike: workout.bike,
  186. program: workout.program,
  187. date: workout.date,
  188. cooldownMin: workout.cooldownMin,
  189. ...activeData(workout.id),
  190. });
  191. } catch(err) {
  192. return res.status(500).json({code: 500, message: err.message || err});
  193. }
  194. });
  195. router.post("/:id/stop", async(req, res) => {
  196. const workout = workouts.find(w => w.id == req.params.id);
  197. if (workout == null) {
  198. return res.status(404).json({code: 404, message: "Workout not found or inactive."})
  199. }
  200. try {
  201. if (workout.driver != null) {
  202. await workout.stop();
  203. }
  204. workouts = workouts.filter(w => w !== workout);
  205. await workout.destroy();
  206. return res.status(200).json({
  207. id: workout.id,
  208. bike: workout.bike,
  209. program: workout.program,
  210. date: workout.date,
  211. cooldownMin: workout.cooldownMin,
  212. ...activeData(workout.id),
  213. });
  214. } catch(err) {
  215. return res.status(500).json({code: 500, message: err.message || err});
  216. }
  217. });
  218. router.delete("/:id", async(req, res) => {
  219. let workout = workouts.find(w => w.id == req.params.id);
  220. if (workout != null) {
  221. return res.status(400).json({code: 400, message: "Workout already active."})
  222. }
  223. try {
  224. workout = await repo.findWorkout(req.params.id);
  225. if (workout == null) {
  226. return res.status(404).json({code: 404, message: "workout not found"})
  227. }
  228. await repo.deleteWorkout(workout);
  229. return res.status(200).json({
  230. id: workout.id,
  231. bike: workout.bike,
  232. program: workout.program,
  233. date: workout.date,
  234. cooldownMin: workout.cooldownMin,
  235. ...activeData(workout.id),
  236. });
  237. } catch (err) {
  238. return res.status(500).json({code: 500, message: err.message || err})
  239. }
  240. });
  241. router.ws("/:id/subscribe", (ws, req) => {
  242. const workout = workouts.find(w => w.id === req.params.id);
  243. if (workout == null) {
  244. ws.send(JSON.stringify({error: "workout not found"}));
  245. ws.close();
  246. }
  247. workout.listMeasurements().then((list) => {
  248. list = list.map(v => {delete v.id; delete v.workoutId; return v});
  249. for (let i = 0; i < list.length; i += 240) {
  250. ws.send(JSON.stringify({workoutStatusBackfill: list.slice(i, i+240)}));
  251. }
  252. }).catch(err => {
  253. ws.send(JSON.stringify({error: `could not list measurements: ${err.message || err}`}));
  254. ws.close();
  255. });
  256. const onWorkoutStatus = (workoutStatus) => {
  257. workoutStatus = {...workoutStatus};
  258. delete workoutStatus.id;
  259. ws.send(JSON.stringify({workoutStatus}))
  260. }
  261. workout.events.on("workoutStatus", onWorkoutStatus);
  262. const onState = (state) => {
  263. ws.send(JSON.stringify({state}))
  264. }
  265. workout.events.on("state", onState);
  266. const onCooldownMin = (cooldownMin) => {
  267. ws.send(JSON.stringify({cooldownMin}))
  268. }
  269. workout.events.on("cooldownMin", onCooldownMin);
  270. ws.onclose = () => {
  271. workout.events.removeListener("workoutStatus", onWorkoutStatus);
  272. workout.events.removeListener("state", onState);
  273. workout.events.removeListener("cooldownMin", onCooldownMin);
  274. ws.removeAllListeners();
  275. };
  276. ws.send(JSON.stringify({
  277. state: workout.state,
  278. workout: {
  279. id: workout.id,
  280. bike: workout.bike,
  281. program: workout.program,
  282. date: workout.date,
  283. cooldownMin: workout.cooldownMin,
  284. },
  285. }));
  286. })
  287. return router;
  288. }