From b742f0f8d6708853a9b8465763d8fdfa491540a4 Mon Sep 17 00:00:00 2001 From: Tobias Reisinger Date: Tue, 30 Apr 2024 10:38:01 +0200 Subject: [PATCH] Split project (keep core) --- .cargo/config.toml | 3 - .env.example | 3 - ...71bf18ede519e6b950933f6751ef9cf7521c9.json | Bin 573 -> 0 bytes ...86549c43e9be2f4ac3b257e836187fa9ed080.json | Bin 734 -> 0 bytes ...417afca55278e269f12203f4fc83096944810.json | Bin 728 -> 0 bytes ...2b2bab2a9923c715295d8b1d937d33844d1a4.json | Bin 364 -> 0 bytes ...f1ff46abbb21dfc3eb3f4f38b251f99d10abd.json | Bin 797 -> 0 bytes ...b94e91e7851a53ebae17a26b19300b83d7dac.json | Bin 277 -> 0 bytes ...3d15ccf840e9f469007d37b6eb47f96f31dc7.json | Bin 442 -> 0 bytes ...d8b11e95d56b2f166326301a0b6722fc1fd44.json | Bin 876 -> 0 bytes ...8f281279353d0b92625c46ef5cd09a672c031.json | Bin 260 -> 0 bytes ...be83d32bb0380b1fb76f90bf4e200636c7f6a.json | Bin 253 -> 0 bytes ...5d9768b3709b347afb04c329b8fb1d3972111.json | Bin 261 -> 0 bytes ...b5abbab1b0c8eec7cf11597267de780299d0d.json | Bin 468 -> 0 bytes ...ebc2015558f66dbcd00cb4ce559d80f2bd023.json | Bin 683 -> 0 bytes ...44f9571c88fad3316593edf0c72ddef24c73e.json | Bin 266 -> 0 bytes ...a11ec04dd0aab26139c19583af00e5baaba56.json | Bin 596 -> 0 bytes ...993ce9e4cafc90204b06977481130ec06d111.json | Bin 691 -> 0 bytes ...2dc6c655102a501d8aa48e03b1fb3dbbad02d.json | Bin 454 -> 0 bytes ...b9bbb0e6c203b721d29cf9149f60bfdd93465.json | Bin 735 -> 0 bytes ...8fdab37cc699720e5484c30697b5566b8d513.json | Bin 670 -> 0 bytes ...f3b2d03cab39116c2732c06e3c74a02fb0367.json | Bin 268 -> 0 bytes ...73396e85db762d482607e4662e788c5542fea.json | Bin 783 -> 0 bytes ...a7a37ef509f50954509557ec985c996960da2.json | Bin 833 -> 0 bytes ...31665a416687772b95d159e5d3e98cde3511c.json | Bin 787 -> 0 bytes ...1e824b99a3888adf5365619ecfed7ae6544a9.json | Bin 866 -> 0 bytes ...ceb0911001eee41256f7b324edc72f5cadcba.json | Bin 759 -> 0 bytes ...3d0d3c690b4e9bccc6f0f5fed2bfd2826b480.json | Bin 265 -> 0 bytes ...07a1bdaa1d82ae3d43d107cf8bac3e9ba4c25.json | Bin 268 -> 0 bytes ...52a342012048741bdcd1ff9461bc46e1cf701.json | Bin 798 -> 0 bytes ...fee8914e253db00e6a648a78e99ade6a4de60.json | Bin 253 -> 0 bytes ...2bf715af1e1ed4d420ac6fa132a94ff352e56.json | Bin 369 -> 0 bytes ...2870941545b095193612956c29d37a5a1b774.json | Bin 362 -> 0 bytes ...ad8c0bacd8d9e73e375dc35e759bdb82369a1.json | Bin 718 -> 0 bytes ...5a94af2e823a8d88ab2aa5ace8c2b56023004.json | Bin 811 -> 0 bytes ...8d7c739d0cb82eea1f8289d012c37bd30c776.json | Bin 255 -> 0 bytes ...4c32a261bb7d1f34577c0f4dc52e318124ecd.json | Bin 874 -> 0 bytes ...120ecf4e5542011b781d8023bdb422580d97a.json | Bin 731 -> 0 bytes ...5b7b4da78ab8acec79c7e7ea51ce5c9414bcb.json | Bin 445 -> 0 bytes ...11691dae057a431fffc27417e4f5504dcfb30.json | Bin 257 -> 0 bytes ...febe44c1250f25989034a3f9e4e8bcb39dece.json | Bin 259 -> 0 bytes ...61be2fe713637f883d175f9b279f0f2f8fd87.json | Bin 712 -> 0 bytes ...1285034c573b86e3193da3995606dee412153.json | Bin 717 -> 0 bytes ...bac76c8233c0feee15f4504bf74ea864716ce.json | Bin 817 -> 0 bytes ...3086dd6e4e204e9e063535b19db666476fe88.json | Bin 900 -> 0 bytes ...646f825be186da276b113c0150aaad26b057c.json | Bin 871 -> 0 bytes ...6c2bc73174fb1921fe1260702f1eab87d979c.json | Bin 254 -> 0 bytes ...97c41c9f71f6a4a3f44221f5ec012c99ebf54.json | Bin 796 -> 0 bytes ...ad12237b9cb69ea6aa4686df6d5262065faa2.json | Bin 702 -> 0 bytes ...d5bfbfbc785fa795254120822b843ce63ff07.json | Bin 252 -> 0 bytes ...22a0a66064e3416e4f5c89d8d83fa6c0536ad.json | Bin 367 -> 0 bytes ...301b502ffd56dd88485cd73b694f6559ccd4e.json | Bin 835 -> 0 bytes ...d962b1da35b5b2b663f759f6af106650951b8.json | Bin 467 -> 0 bytes ...2697741fb210ea8bfc3d61f1508106c0b076e.json | Bin 684 -> 0 bytes ...fb7b09f6355ecbc8b6e1085d5f2f9a08cac3f.json | Bin 783 -> 0 bytes ...d36ed21da18ceed5b71e51a1e749bab466422.json | Bin 481 -> 0 bytes ...f420d3abb08358a1abe301dc7e08693fbef4d.json | Bin 678 -> 0 bytes ...f4a2d40abdf3843d0c815399ea9d267cade5a.json | Bin 574 -> 0 bytes ...b28a92524b8f556840b76f76c8f317059e668.json | Bin 311 -> 0 bytes ...ada06827a0e5a2d2dbd48bfb8e04f973a6131.json | Bin 283 -> 0 bytes ...7be750a7bcf5d1185935e8386671c69dc7270.json | Bin 560 -> 0 bytes Cargo.toml | 38 ++- Makefile | 29 +- emgauwa-core/build.rs => build.rs | 4 +- config/controller.pkl | 56 ---- config/core.pkl => core.pkl | 0 emgauwa-common/Cargo.toml | 32 --- emgauwa-common/build.rs | 3 - emgauwa-common/src/constants.rs | 10 - emgauwa-common/src/db/controllers.rs | 184 ------------- .../src/db/junction_relay_schedule.rs | 146 ----------- emgauwa-common/src/db/junction_tag.rs | 48 ---- emgauwa-common/src/db/macro.rs | 166 ------------ emgauwa-common/src/db/macro_action.rs | 99 ------- emgauwa-common/src/db/mod.rs | 55 ---- emgauwa-common/src/db/model_utils.rs | 137 ---------- emgauwa-common/src/db/relays.rs | 177 ------------- emgauwa-common/src/db/schedules.rs | 209 --------------- emgauwa-common/src/db/tag.rs | 91 ------- emgauwa-common/src/drivers/gpio.rs | 35 --- emgauwa-common/src/drivers/mod.rs | 19 -- emgauwa-common/src/drivers/null.rs | 26 -- emgauwa-common/src/drivers/piface.rs | 52 ---- emgauwa-common/src/errors/api_error.rs | 22 -- emgauwa-common/src/errors/database_error.rs | 85 ------ emgauwa-common/src/errors/emgauwa_error.rs | 161 ------------ emgauwa-common/src/errors/mod.rs | 7 - emgauwa-common/src/lib.rs | 8 - emgauwa-common/src/models/controller.rs | 86 ------ emgauwa-common/src/models/macro.rs | 42 --- emgauwa-common/src/models/macro_action.rs | 56 ---- emgauwa-common/src/models/mod.rs | 68 ----- emgauwa-common/src/models/relay.rs | 107 -------- emgauwa-common/src/models/schedule.rs | 41 --- emgauwa-common/src/models/tag.rs | 49 ---- emgauwa-common/src/settings.rs | 67 ----- emgauwa-common/src/types/emgauwa_uid.rs | 103 -------- emgauwa-common/src/types/mod.rs | 29 -- emgauwa-common/src/types/request.rs | 95 ------- emgauwa-common/src/types/schedule_uid.rs | 171 ------------ emgauwa-common/src/utils.rs | 114 -------- emgauwa-controller/Cargo.toml | 32 --- emgauwa-controller/src/app_state.rs | 173 ------------ emgauwa-controller/src/driver.rs | 22 -- emgauwa-controller/src/main.rs | 124 --------- emgauwa-controller/src/relay_loop.rs | 150 ----------- emgauwa-controller/src/settings.rs | 106 -------- emgauwa-controller/src/utils.rs | 66 ----- emgauwa-controller/src/ws/mod.rs | 247 ------------------ emgauwa-core/Cargo.toml | 31 --- migrations/20231120000000_init.down.sql | 8 - migrations/20231120000000_init.up.sql | 161 ------------ {emgauwa-core/src => src}/app_state.rs | 0 {emgauwa-core/src => src}/handlers/mod.rs | 0 .../src => src}/handlers/v1/controllers.rs | 0 .../src => src}/handlers/v1/macros.rs | 0 {emgauwa-core/src => src}/handlers/v1/mod.rs | 0 .../src => src}/handlers/v1/relays.rs | 0 .../src => src}/handlers/v1/schedules.rs | 1 - {emgauwa-core/src => src}/handlers/v1/tags.rs | 0 .../handlers/v1/ws/controllers/handlers.rs | 0 .../handlers/v1/ws/controllers/mod.rs | 0 .../src => src}/handlers/v1/ws/mod.rs | 0 .../src => src}/handlers/v1/ws/relays/mod.rs | 0 {emgauwa-core/src => src}/main.rs | 0 {emgauwa-core/src => src}/settings.rs | 0 {emgauwa-core/src => src}/utils.rs | 0 127 files changed, 38 insertions(+), 4016 deletions(-) delete mode 100644 .cargo/config.toml delete mode 100644 .env.example delete mode 100644 .sqlx/query-03bfe5bd71d673a621b6ad6af5f71bf18ede519e6b950933f6751ef9cf7521c9.json delete mode 100644 .sqlx/query-0874e71c6206efc82528cc450bc86549c43e9be2f4ac3b257e836187fa9ed080.json delete mode 100644 .sqlx/query-08c517120fcfb4534a3ff540910417afca55278e269f12203f4fc83096944810.json delete mode 100644 .sqlx/query-1348af1c13719ffcd72c2a4c6712b2bab2a9923c715295d8b1d937d33844d1a4.json delete mode 100644 .sqlx/query-1cb4cc57ff361d6d84a0c8e8f5df1ff46abbb21dfc3eb3f4f38b251f99d10abd.json delete mode 100644 .sqlx/query-1eda8cf54e553e8e892ac63a31cb94e91e7851a53ebae17a26b19300b83d7dac.json delete mode 100644 .sqlx/query-20e99a281e5e3e9c9d7375425d93d15ccf840e9f469007d37b6eb47f96f31dc7.json delete mode 100644 .sqlx/query-2551c285e3e223311cff8e32022d8b11e95d56b2f166326301a0b6722fc1fd44.json delete mode 100644 .sqlx/query-2e3528a386066c3fc11be1259298f281279353d0b92625c46ef5cd09a672c031.json delete mode 100644 .sqlx/query-3adb7ab9cacd5a84d882e4c9860be83d32bb0380b1fb76f90bf4e200636c7f6a.json delete mode 100644 .sqlx/query-3b5a701d0ace12125c573680bcd5d9768b3709b347afb04c329b8fb1d3972111.json delete mode 100644 .sqlx/query-3fe383ea9ed4965e25d54eea08fb5abbab1b0c8eec7cf11597267de780299d0d.json delete mode 100644 .sqlx/query-457e9d4808332255ed7354a28e6ebc2015558f66dbcd00cb4ce559d80f2bd023.json delete mode 100644 .sqlx/query-486ed307f718754a3d4ea2c6fe944f9571c88fad3316593edf0c72ddef24c73e.json delete mode 100644 .sqlx/query-49afbeaa1d32f8c2e6fea7a2d57a11ec04dd0aab26139c19583af00e5baaba56.json delete mode 100644 .sqlx/query-4f5408e64f5e6a8dd923c3b147f993ce9e4cafc90204b06977481130ec06d111.json delete mode 100644 .sqlx/query-52958684fa52b7a4753cd4356482dc6c655102a501d8aa48e03b1fb3dbbad02d.json delete mode 100644 .sqlx/query-5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465.json delete mode 100644 .sqlx/query-66a141b71041a7827f1932e6e288fdab37cc699720e5484c30697b5566b8d513.json delete mode 100644 .sqlx/query-711398eb64710a25cc818167be8f3b2d03cab39116c2732c06e3c74a02fb0367.json delete mode 100644 .sqlx/query-7519da166e2e0b6de4c02559fc173396e85db762d482607e4662e788c5542fea.json delete mode 100644 .sqlx/query-772c710d542c7f5aaa49069fe12a7a37ef509f50954509557ec985c996960da2.json delete mode 100644 .sqlx/query-7b3ee1cad84a146699b9010be6d31665a416687772b95d159e5d3e98cde3511c.json delete mode 100644 .sqlx/query-7bbe1a982c77194feba2ff610b01e824b99a3888adf5365619ecfed7ae6544a9.json delete mode 100644 .sqlx/query-7cd5b42013b4e6a37a670e55cd3ceb0911001eee41256f7b324edc72f5cadcba.json delete mode 100644 .sqlx/query-7e7cdf2650c08feb10a35275e693d0d3c690b4e9bccc6f0f5fed2bfd2826b480.json delete mode 100644 .sqlx/query-87b7396fb761030e25836cb90ed07a1bdaa1d82ae3d43d107cf8bac3e9ba4c25.json delete mode 100644 .sqlx/query-8bdd41a11fd2ca0440b2d6c0dc752a342012048741bdcd1ff9461bc46e1cf701.json delete mode 100644 .sqlx/query-90ed2cd2f8161552dae06ab2d9cfee8914e253db00e6a648a78e99ade6a4de60.json delete mode 100644 .sqlx/query-921a0775f75d9e2f67f604265872bf715af1e1ed4d420ac6fa132a94ff352e56.json delete mode 100644 .sqlx/query-96a0b9960daa8a10e04e22cba592870941545b095193612956c29d37a5a1b774.json delete mode 100644 .sqlx/query-96f34b8654265ea5ab5210ab5dcad8c0bacd8d9e73e375dc35e759bdb82369a1.json delete mode 100644 .sqlx/query-a0c9c1a108c6560b4f073c866415a94af2e823a8d88ab2aa5ace8c2b56023004.json delete mode 100644 .sqlx/query-a1f5699889cfabb3f681e6bb71a8d7c739d0cb82eea1f8289d012c37bd30c776.json delete mode 100644 .sqlx/query-a64694ec1a81472a05a68b1caf64c32a261bb7d1f34577c0f4dc52e318124ecd.json delete mode 100644 .sqlx/query-a69f0bf9b3fefd7914502aa15fa120ecf4e5542011b781d8023bdb422580d97a.json delete mode 100644 .sqlx/query-a6dc153657cb3fefb5ba5b763dc5b7b4da78ab8acec79c7e7ea51ce5c9414bcb.json delete mode 100644 .sqlx/query-a8b2d9cfd386b5f9ad5b76ef08711691dae057a431fffc27417e4f5504dcfb30.json delete mode 100644 .sqlx/query-a94cb95af7e6c13e0e155c74de2febe44c1250f25989034a3f9e4e8bcb39dece.json delete mode 100644 .sqlx/query-ab8dafa95af67dc06074e83e1ec61be2fe713637f883d175f9b279f0f2f8fd87.json delete mode 100644 .sqlx/query-b41855e635ac409559fa63cba4c1285034c573b86e3193da3995606dee412153.json delete mode 100644 .sqlx/query-b6f6fd898dc3f1dbe2c39bf0445bac76c8233c0feee15f4504bf74ea864716ce.json delete mode 100644 .sqlx/query-b91f6aab0bdb316633b3a0d75303086dd6e4e204e9e063535b19db666476fe88.json delete mode 100644 .sqlx/query-bcd9d4dd3641e6c84262ed5f6c5646f825be186da276b113c0150aaad26b057c.json delete mode 100644 .sqlx/query-c138a9c659a7410e9935ad3f6a56c2bc73174fb1921fe1260702f1eab87d979c.json delete mode 100644 .sqlx/query-c30156fb112fcc28f08fbbec04197c41c9f71f6a4a3f44221f5ec012c99ebf54.json delete mode 100644 .sqlx/query-c9437ff0c3014b269dcb21304fbad12237b9cb69ea6aa4686df6d5262065faa2.json delete mode 100644 .sqlx/query-cb0d76a3a1cfa439d48056c865cd5bfbfbc785fa795254120822b843ce63ff07.json delete mode 100644 .sqlx/query-cbda6fd5137f3698537262772df22a0a66064e3416e4f5c89d8d83fa6c0536ad.json delete mode 100644 .sqlx/query-cdcae4768f7b62390e5e5da850e301b502ffd56dd88485cd73b694f6559ccd4e.json delete mode 100644 .sqlx/query-db48fb93e9f22ee7da0786ae913d962b1da35b5b2b663f759f6af106650951b8.json delete mode 100644 .sqlx/query-e9386ab7ecbe4ce13f8ee5ee5852697741fb210ea8bfc3d61f1508106c0b076e.json delete mode 100644 .sqlx/query-e94ef5bc8b267d493375bb371dcfb7b09f6355ecbc8b6e1085d5f2f9a08cac3f.json delete mode 100644 .sqlx/query-ea4b06aaad9436096e20a53d81fd36ed21da18ceed5b71e51a1e749bab466422.json delete mode 100644 .sqlx/query-ee7da56331bece2efe21b55dbd5f420d3abb08358a1abe301dc7e08693fbef4d.json delete mode 100644 .sqlx/query-eee8820f59927c1560a5fd461b9f4a2d40abdf3843d0c815399ea9d267cade5a.json delete mode 100644 .sqlx/query-f75318134ec1facc3de6b04232cb28a92524b8f556840b76f76c8f317059e668.json delete mode 100644 .sqlx/query-f7b90a6a0af3f7d7c96158f96f1ada06827a0e5a2d2dbd48bfb8e04f973a6131.json delete mode 100644 .sqlx/query-fde0dca1aba5b490a9f5f6006677be750a7bcf5d1185935e8386671c69dc7270.json rename emgauwa-core/build.rs => build.rs (72%) delete mode 100644 config/controller.pkl rename config/core.pkl => core.pkl (100%) delete mode 100644 emgauwa-common/Cargo.toml delete mode 100644 emgauwa-common/build.rs delete mode 100644 emgauwa-common/src/constants.rs delete mode 100644 emgauwa-common/src/db/controllers.rs delete mode 100644 emgauwa-common/src/db/junction_relay_schedule.rs delete mode 100644 emgauwa-common/src/db/junction_tag.rs delete mode 100644 emgauwa-common/src/db/macro.rs delete mode 100644 emgauwa-common/src/db/macro_action.rs delete mode 100644 emgauwa-common/src/db/mod.rs delete mode 100644 emgauwa-common/src/db/model_utils.rs delete mode 100644 emgauwa-common/src/db/relays.rs delete mode 100644 emgauwa-common/src/db/schedules.rs delete mode 100644 emgauwa-common/src/db/tag.rs delete mode 100644 emgauwa-common/src/drivers/gpio.rs delete mode 100644 emgauwa-common/src/drivers/mod.rs delete mode 100644 emgauwa-common/src/drivers/null.rs delete mode 100644 emgauwa-common/src/drivers/piface.rs delete mode 100644 emgauwa-common/src/errors/api_error.rs delete mode 100644 emgauwa-common/src/errors/database_error.rs delete mode 100644 emgauwa-common/src/errors/emgauwa_error.rs delete mode 100644 emgauwa-common/src/errors/mod.rs delete mode 100644 emgauwa-common/src/lib.rs delete mode 100644 emgauwa-common/src/models/controller.rs delete mode 100644 emgauwa-common/src/models/macro.rs delete mode 100644 emgauwa-common/src/models/macro_action.rs delete mode 100644 emgauwa-common/src/models/mod.rs delete mode 100644 emgauwa-common/src/models/relay.rs delete mode 100644 emgauwa-common/src/models/schedule.rs delete mode 100644 emgauwa-common/src/models/tag.rs delete mode 100644 emgauwa-common/src/settings.rs delete mode 100644 emgauwa-common/src/types/emgauwa_uid.rs delete mode 100644 emgauwa-common/src/types/mod.rs delete mode 100644 emgauwa-common/src/types/request.rs delete mode 100644 emgauwa-common/src/types/schedule_uid.rs delete mode 100644 emgauwa-common/src/utils.rs delete mode 100644 emgauwa-controller/Cargo.toml delete mode 100644 emgauwa-controller/src/app_state.rs delete mode 100644 emgauwa-controller/src/driver.rs delete mode 100644 emgauwa-controller/src/main.rs delete mode 100644 emgauwa-controller/src/relay_loop.rs delete mode 100644 emgauwa-controller/src/settings.rs delete mode 100644 emgauwa-controller/src/utils.rs delete mode 100644 emgauwa-controller/src/ws/mod.rs delete mode 100644 emgauwa-core/Cargo.toml delete mode 100644 migrations/20231120000000_init.down.sql delete mode 100644 migrations/20231120000000_init.up.sql rename {emgauwa-core/src => src}/app_state.rs (100%) rename {emgauwa-core/src => src}/handlers/mod.rs (100%) rename {emgauwa-core/src => src}/handlers/v1/controllers.rs (100%) rename {emgauwa-core/src => src}/handlers/v1/macros.rs (100%) rename {emgauwa-core/src => src}/handlers/v1/mod.rs (100%) rename {emgauwa-core/src => src}/handlers/v1/relays.rs (100%) rename {emgauwa-core/src => src}/handlers/v1/schedules.rs (99%) rename {emgauwa-core/src => src}/handlers/v1/tags.rs (100%) rename {emgauwa-core/src => src}/handlers/v1/ws/controllers/handlers.rs (100%) rename {emgauwa-core/src => src}/handlers/v1/ws/controllers/mod.rs (100%) rename {emgauwa-core/src => src}/handlers/v1/ws/mod.rs (100%) rename {emgauwa-core/src => src}/handlers/v1/ws/relays/mod.rs (100%) rename {emgauwa-core/src => src}/main.rs (100%) rename {emgauwa-core/src => src}/settings.rs (100%) rename {emgauwa-core/src => src}/utils.rs (100%) diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 44fa6b4..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,3 +0,0 @@ -[alias] -format = "+nightly fmt" -lint = "clippy --all-targets --all-features -- -D warnings" diff --git a/.env.example b/.env.example deleted file mode 100644 index f86ad04..0000000 --- a/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -#EMGAUWA_CONTROLLER__LOGGING__LEVEL=DEBUG - -#EMGAUWA_CORE__LOGGING__LEVEL=DEBUG diff --git a/.sqlx/query-03bfe5bd71d673a621b6ad6af5f71bf18ede519e6b950933f6751ef9cf7521c9.json b/.sqlx/query-03bfe5bd71d673a621b6ad6af5f71bf18ede519e6b950933f6751ef9cf7521c9.json deleted file mode 100644 index ce5073bfb449296706a9e1ce0edec68ad8649f48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 573 zcma)2T}#6-6n*cn2zeAAn%1UP5J9OZOvE|xL1ZM&jSZyjY*TQI{dbeJ(G3yILm=m# zkDIfL5W<45Th;)+K=}D-p$yyf19Xy!+P7k zf!-ArzJZtOy*3sgo;!aNx1-jKYu9n8R5Cq4Nd5hoqU`N0aLtC=l^Ca7LdHc-1kF=M o6T&GIl*vrygiCS`0y0tn%0NQhP>q! zh+YADy3SS^8bK-0DLO;vcbDiB;9ao6zDHUCEpKFD<#tBK2=Q9=8{Hb<*#T%OxT4=o z3O8;sGg3D~YNpIcV0Rr4?uISoQkS*e`bKw2gs0-(ca4&^AA9C$!^8A*KWGO2M(-Ut z%uoK!?*qucb13|sgmda;?8RHwm>6|AdNTB4D#DsDl#X-So5=RRjg|H}qlo8`$LE;o>n9ONf NvIU5M26(ogeE|Xrv)upy diff --git a/.sqlx/query-08c517120fcfb4534a3ff540910417afca55278e269f12203f4fc83096944810.json b/.sqlx/query-08c517120fcfb4534a3ff540910417afca55278e269f12203f4fc83096944810.json deleted file mode 100644 index b29819bd5882d448fac7fb50c95ed6cb4dbc8729..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 728 zcmbtSL2JVx6u$RYh^Kb+powXdUCIt+6iUX@c9$`NG1@q+=@N4&rT=}J)WM*Gv3U^v z-h1Eo{GR6#0I)1_O%EL21Gsr!OT*#R!5H_YVezzmw>VdK+@#kv8@>7~Z&{c*|fW_m#0g(C1z)g}PIU z7Rt@jk55q5axeSsVw&zN;Ld}p-Fr4z(YA84Loti+90y1+QIIGkG!!BtD8Y!}ITegX cQ815rFiQju0u%}&SRA5cmJkwS1V@+AC*z2(NB{r; diff --git a/.sqlx/query-1348af1c13719ffcd72c2a4c6712b2bab2a9923c715295d8b1d937d33844d1a4.json b/.sqlx/query-1348af1c13719ffcd72c2a4c6712b2bab2a9923c715295d8b1d937d33844d1a4.json deleted file mode 100644 index f7313fdb98e4efc236058c393aad199e7bd84f36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 364 zcmXv}O-sWt7{2#cgq%J2CY@~s@gNn2iC6^>GRB*a+CZAlv^|W`|8A0YIVA9W=aLaZ zl{h-=HN7YF^J8x&O>ZLnH*0@RV7;%Oz6g-#=KYQIDE%;!@0Yr%$!rvPAdjm-=}~$k zV$dZ7ga|pS2xj(TGD%1 zmGp!@d}Go+7Oo3IVq<>0DFBeP%cga+wJ2=ddY#sy9rYR*?j)ogj$P~oo|o8y1YSVZ pwo(;LGR);&0ZcG~f?-uLE(_SPYNtv8s^W#>yv%b2nA2>@{sD6XSvUXy diff --git a/.sqlx/query-1cb4cc57ff361d6d84a0c8e8f5df1ff46abbb21dfc3eb3f4f38b251f99d10abd.json b/.sqlx/query-1cb4cc57ff361d6d84a0c8e8f5df1ff46abbb21dfc3eb3f4f38b251f99d10abd.json deleted file mode 100644 index 5be1688e89bdf4fccb72b2aecd37d3b2ed744a13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 797 zcmb7?T}#6-6o&8jD?%>Ci>2*qCx|eqC``mT*oDZ*rwe4sOjB`;{dbeJ-3M$idlBe+ z&UwzuIcL)Y05USyc*)@{fbVZ(={UR&@^{VcI*R4Ee3*hO@HlyW0n}(`jZ)lJVD?l_ zN>ED%?!bLh#dt-nB!RZ^6wjeX)k|G@;Ufy54a8vASh83iG+$yYlNu{u(hpXe54!b= z&!rY7n0nSO&tMzec3Dl!kPkbucXt|%x+0%^gdQnFE+NMb2avt%p3;GcBjp!=Q>1g8Sc}97h=E! g5+chnAtXUe(3F!@WFjp{GC*QDWT=k`gS~C<4>&@rij(IKb=@=$5<*BsVqk!_h@q;`**=IRH!XHDpsN3!yO!~N@7;Ij z3P1(-IpBfR9jMLQOB_kf1piEwe+9n3K0R(bD88pKB`UD#wjdX94-YdqQI5w@B*4dW zx9%VY2{%ii;&XIjKeLs+{+Oo2P#Em?@`%w1GwVjmLT@vs>-gP|49s%ZzH)wLl@wq2lK88q6vc1f+K#?~6Sb4a?u#mec{%;(OU&;<0V Gx>SEpRZ6Y^ diff --git a/.sqlx/query-20e99a281e5e3e9c9d7375425d93d15ccf840e9f469007d37b6eb47f96f31dc7.json b/.sqlx/query-20e99a281e5e3e9c9d7375425d93d15ccf840e9f469007d37b6eb47f96f31dc7.json deleted file mode 100644 index 776039b179b55e5d9342bbc2a4a5952f758b7321..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmY*V%WA_g5WMFrgw7@S?Wj_&r=8B#y*XyDrubbtX{0v4| z)tbsNHn;aVVwIBQ!K>z2eZCZBaif`cp!qnl9w%=Jz35c!GMz9BrV*_k4yH%o%LC-n z@Xc#{H1m1~qNeSnGF+o3a4(t`wc`;gWt!H#Z3};vUV3MLeos_-6#Twl5%N)FEUB$i5J9atnH$!Dh)PMDR~OAvO;dD?{dbde0R@L^p(Xd; z-6i+3>LG-Akt?F=S-YB`81HG*@fNJ z*~3=eC%_wL%kH2Yno^6x1+cOQw?{8)*)CLLfkzLZO~DsKR)TxF!z`uNTqq_jW#H25 z9&DEHkPB7SF2|8F$pPLJ*P*Mkbp1Fe`+una(k4I9U)nSHGCN7PivslD8>idEciwne zE2C>ELEC}+Z4&Kf;!}F=dht8c_I?`BZXzKsoQapYu>kSCn>?{lm~0@} z{cb?+O`lj{fo diff --git a/.sqlx/query-2e3528a386066c3fc11be1259298f281279353d0b92625c46ef5cd09a672c031.json b/.sqlx/query-2e3528a386066c3fc11be1259298f281279353d0b92625c46ef5cd09a672c031.json deleted file mode 100644 index fe474aeaf7b96d6adac0268d7b0465ba964399c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260 zcmXZVy>7!G6o%p5SAm!_7zo5vs=Ab_L!zi|r4CUP!2#Du{ztxn#yQYc=F1Y&Z^l=%HD89j)%zXkEu`^eVUFW4k?(g?8xlQvCNGa8 zA0|lA<=7P?n1Qid`e8yi9CH`xY!m)Bhm`d(L#^XaJ0r0B==Q%Imaem|%fZ7DU+zv@ zJGCg32O)W7MWsrmKs3OZ3ye$6b=B~S@r@Qz_|j>vxP-L|jRaq6t2P`;!Dw+S{s02$ BL@xjU diff --git a/.sqlx/query-3adb7ab9cacd5a84d882e4c9860be83d32bb0380b1fb76f90bf4e200636c7f6a.json b/.sqlx/query-3adb7ab9cacd5a84d882e4c9860be83d32bb0380b1fb76f90bf4e200636c7f6a.json deleted file mode 100644 index 59ce879ae84f4974a31a99a9cfc0db080ebfbd2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 253 zcmXZVF;BxV5Cz~pzvASX!Ht9CAXQy3VkklcB!;T0?reudO48Oz22}OGb8cC`d+*&P z0}!4LBMw14f%yE`c5@I9iO)-jzX_l1)_z%dso&pVKo^e_e81Y-Lg!%(n|s=a$;FP+ z%7xq9cgKDi#))CKPlHS+jQnU0G3h3gI;4es`Qxw&X@YFUIr vPbE_2(D_F+g*PS-g{zE~6pZpJCz8rYS~XParjpbYA(v7s?aD@@$gbHRv&chS diff --git a/.sqlx/query-3b5a701d0ace12125c573680bcd5d9768b3709b347afb04c329b8fb1d3972111.json b/.sqlx/query-3b5a701d0ace12125c573680bcd5d9768b3709b347afb04c329b8fb1d3972111.json deleted file mode 100644 index 390622e701010d266e72e70ba916e9e4f0b8fe69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 261 zcmXZUON+uV6o%n_f5jnd7r9u|3d0~+n8hr-4YLrDoFv*p?2KuX`RK`@7qeDghK z<{S_Lob41nAlwjmd))|)aBbnQ8OM_Y>rH*XC0FF3d3h$9?kDp8SU0tOl3Q}O&^b=+ zD44Z58@VxP2RR?qWPlF`khS27K2dNdx0sO(BNvKFBMEH}%K?2l;!db;u-!ZtAP=@VQXoNJq0Fjia5$XF((Oy*5DPxN9$-vrs{1VN=5{LR>D1fdk`9wHL0TbCt31Ov<5`MG!?I%;WsI_jg>8x07HtGd zTA^!nGqfnMvem+)whrMSvN8RtwoZ5t0@@csF{Wx2+P&slnpWwW8`nbo9K(mZ%^H@v zE*n45W8Gyb-WPY#HAH$pj^zFndK{aa`3QY$8gULeBpg6~r=|z$5qbgN-8twrw7OCD z@0}#4=tqNx)qD(AyymStZwIYELtGM`>(8ol!Z>*MOT=|=3>U^7xV6o%nFS7CW(Ah8oCm8vc^>Oh4ENF5MDj?ZypDL<`~3>D(udkn0Pzw`Q< zFvg*|45ml^!uVmo^)vESX^~d~!L6;z0MKR9DuFz?CI5&0 BNIw7o diff --git a/.sqlx/query-49afbeaa1d32f8c2e6fea7a2d57a11ec04dd0aab26139c19583af00e5baaba56.json b/.sqlx/query-49afbeaa1d32f8c2e6fea7a2d57a11ec04dd0aab26139c19583af00e5baaba56.json deleted file mode 100644 index 90e1cca5eef580dd7c4cb6eedc308bdf2f604a2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 596 zcma)&Piw+J5XJ9)ieXP$JS5RX?L}IUB1lSO?4^`4o1N5!HL>{v74f@wcjLB7d*~h( z_RX8$yq#kYfC>Iq;EvcWu=UeQHpGTT{%EN>7;zb|7fA}sIDLd2W>r~3zm;MLrfUGt zcdN}}4gL8L<^xC;=_ZMn@dMm+7(sPbNpA2*jn&|+RPC;)weVsEI2UZuC6U6m8%?*A zm5>E0-Qt;S-C@mPPj9lwOPjK2qA@!cPtMhiG=ApDI{M#e-!*CT75ZM4{6Azn;R5oL zI=E1;&?$X3-)I~DwX?E4R(ib+Rc2A{8!VCU+eQn4^`5`UidHG)%Iw&!G)GmFoBI3F zjncP8ur1bGv&8WXb54l97zOz>3nFTbGflxU_U|U?0uCH54+%N< z+;cAX>?R0dS*;Ck0be8h`m#_C@MVC%2C&-*mW%Rkg)Y$j^7#o_(0tpYw}*0BA|=rc zx*hovdSO)+bauYqD~L`Ht?51R9szVH#A32kGPVcIw{%u2!?kZAVTkcyx81;6nYs(( zJQ|lXd?@Y{YqSh}oRs66>POo27kZ?MQ5Dtv zCp&uJBLZY;#AeFSGRgzyCH)|^MdKxTNSz>H{lRz?-tFdQfmCn z?!HD9bU1clIaWTfrfz79tmG@CD^>|fD^}B{l%f_jV?|yf%L~~c(njSKFIrwn&Qa#2 IgzT360LoEp$p8QV diff --git a/.sqlx/query-5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465.json b/.sqlx/query-5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465.json deleted file mode 100644 index 67ea7e8877419a84a9f133d916ac4c28076d8f5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 735 zcma)3%WA_g5WMFr7M+sfgX4!B_fmQY#SmQTxV@ATBuj1#ik!+eG=%(nB{_lyQW{|- zJhMBq>>MWmh|!HCJC5!FtzTA6&yi=~uYs$B0keF)EK11o@);BtwU>6QS~2Wor-641 zz?rb58?Rxu-C(+c zau9-0VZwCT)kJjM+5Yo0`s&;2p)KjQCc1cm<2p!anna6)Vpg*>#w26`_GwBf^*OEr bKcq=8XF*!k={!nn&DD&hVT5BeIZb{5`#-Yk diff --git a/.sqlx/query-66a141b71041a7827f1932e6e288fdab37cc699720e5484c30697b5566b8d513.json b/.sqlx/query-66a141b71041a7827f1932e6e288fdab37cc699720e5484c30697b5566b8d513.json deleted file mode 100644 index c08e9fc2b8fceb7c3391e3f4bb68372d718609f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 670 zcma)3$!fze5WV{=gpQ>L%ZqIH*pfmar8Ld0l%Qo?3l!V+0u3SmUP-n{dS>Ga(DUZ)74@P5Icn%9mV{n(id*&3 zsEkfDdfeB&0X`jo=7AgfASIpLVn)(*Qq@cwNob>|2M>o9cB(ELyTnU9&;-xJlV?ql zmXE#jc+Hx4ks&XvA9cfTadrt8Cg-iA3v^+lQlI$f?r#@6Yb>HTT8hu9~lj zid)v1iw@A)Gx(D5MtxUnnRNN*PHA v&q$aMmgP}ak|K_PLX_vFWIWDHe?jC2`3x=bK%qwC+cpJKEwmf&tr zH#cXU9Xk?cgi!O^J3TXkTX~OIi&|*oe0@|-wp^igYA%FEX_Q2+y+SjyTpk@)+OXPRX?FHmY^NTL6-w5Q;HM@F4e?lW4<*qS8rHAKrh+?zXTJs?olUM`cNlhT1okfu&pIJ|(uCH+p2`47UuJH+aX1#=OdyTaLNp~xzJ7xc#%lJ~l)pMcD zim!gT!+ekZ=a<~4TFc-5!|>mGD%w)xuEzR1h?{qFU5Jmh^=k~=VNFPNQqo%b8;K{; zP}xFE{m)O|gnNTITGMvz&!a4|38xt$fFZ33hn%IjC?*Zd%JFzYpelG>63p`oleC1K R5DaBiv7*Rv0~C!8qaR9Gxhntw diff --git a/.sqlx/query-772c710d542c7f5aaa49069fe12a7a37ef509f50954509557ec985c996960da2.json b/.sqlx/query-772c710d542c7f5aaa49069fe12a7a37ef509f50954509557ec985c996960da2.json deleted file mode 100644 index 6915ce7920e8c9c3df5cb1e450b130536602b792..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 833 zcmbtSO-sZu5WVlO2sw&}w%ZRQh=^7cR>f8DAW~+NsSRe+wI4`X_P?8?&CLaM15L@B zd2c3pvz-w_#j4P-MtVx<)8j&RNRKW2*<&*}Fkj@C&*X?)FCXs68hBF|z&ly%mb~2L z%Y4G5AZO&9T-;xgFT@W4gFTO80$bk5l`XP0;zpj=s$c8Y0Iv=pQ@{m%KT`t6k&{lRFb`|1tr9m<1^-F^nLCd4^>YF#9F3-I5GB QJ4rZWDN6$Z5uNR3-#^I64*&oF diff --git a/.sqlx/query-7b3ee1cad84a146699b9010be6d31665a416687772b95d159e5d3e98cde3511c.json b/.sqlx/query-7b3ee1cad84a146699b9010be6d31665a416687772b95d159e5d3e98cde3511c.json deleted file mode 100644 index c9041612822aac535347d355dd5cf63db86447e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 787 zcma)4-%GmJb#%83HVaOpAA%714fhReu3&V1K#A&zd#T1?D-Ja=yrYx z(0DqHV)Qf{PtiwHq*9bcBIydvx^dsCC4!^t1HG@J*T*Q1I^@0*XzOlTYaHsd5=>j( zDXYqCTIS8Vs1@*L0JKk-q@7N1L)Vz1lob~R%@t*!=WRbMw;M=Au`KoE<3jo_-Y36p zRohb6W2@Ypk|vgmFd>%Vv6oKo)?H>-JX zOYw$Q>Nk>5=`+|R;aIFPsQ_YX_e;b@ljoG>`hxagOPbeUyZ#fjebsC9a7OD)-z#7a x07)qyxsZ*4X1i_Gn9w%N19(N!dr5qfOkQDd6dq3Ah$A16- diff --git a/.sqlx/query-7bbe1a982c77194feba2ff610b01e824b99a3888adf5365619ecfed7ae6544a9.json b/.sqlx/query-7bbe1a982c77194feba2ff610b01e824b99a3888adf5365619ecfed7ae6544a9.json deleted file mode 100644 index 217c70fc6aba5215f9df7e8aa46ae8b28d7ba2b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 866 zcma)4TWi8F7=8CwgglNRw$|DXhV0TYhg0T+J_YEWTiwou5) zxg_W7riBn@G?ruu_yXbQrx7m!pKJK5f_!bjXcXPdP?0QvRRW6k8M>QJ9`^tmj>pjy zJxqpU^ij#AlNfYW5wT{ber6d4sNP>tEqTJa`ji1kn1tnlJJzjFG>ZthPx{f%Ss4Bg`S`tSV}|z zHuaxHovXga4=+fu(DUk33JwVaH|h7C&`W_5H%)z~Lpu%xu1CX=bb}xuEbVpup6`T^ Qq`>+F{GR8L5Vy9iUr?^-aR2}S diff --git a/.sqlx/query-7cd5b42013b4e6a37a670e55cd3ceb0911001eee41256f7b324edc72f5cadcba.json b/.sqlx/query-7cd5b42013b4e6a37a670e55cd3ceb0911001eee41256f7b324edc72f5cadcba.json deleted file mode 100644 index 8d846ef0202322841d113d1aee256c51ea4b9b13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 759 zcmbtS%SyvQ6y5hLhFP_eMczR-;vz_)L`+*ZB4HjkW^_8Kc_5+m-t7y@(d zJ?GrVoE8XSP76g2fNv1qK5j$@*fH=&56xh}b+%3O9a?9*2lUn})`?mb4M;K+Evta{ z643G!;GL7fK~q3*2iagD_zFGUZT9IFEpHuU&I;w}Zl7oC>>gcBejHlX2x{s$X@6Y- zt7U&stp=V)0L_6hXiFA+q#I0A>V^wNq^7v0iRr`LaD+mrvL4$}T^!;$c(z%SrOC%u zd2Z>lyl07;@HcrosLT5PzxBV3|FT8!JBx8rFHy|y!wl;j=oM3S!`lW#Ut4BtBYLLS&}(CiaLhFhEZ5HRjtjl+7_BbV EA5k$#UjP6A diff --git a/.sqlx/query-87b7396fb761030e25836cb90ed07a1bdaa1d82ae3d43d107cf8bac3e9ba4c25.json b/.sqlx/query-87b7396fb761030e25836cb90ed07a1bdaa1d82ae3d43d107cf8bac3e9ba4c25.json deleted file mode 100644 index 63d4cfd0b89139c9471922e880c974556311ecdf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 268 zcmXZVv2Md46ouj4Pl1>_fJuy9De6+H4ymfTkupS)b%C3hijdZZp^EbEg{14pxj&bR z5Nh=h;Dq#%(9e&xi%1^||0JA#3qGs0+9>kUzrT^M6ijsIFhn?#?W^h)nazkcc^sT2 zEAn(dwm6%~=@PxF}{--uJu`z&kkj$_QgZ=>t~HYD#s`x4yk z^UKZY2=kbQwo*&cH3uyl&IH4H*^0*Kj-h1|xV8Y?wl$!z3t>5v=Fn;|0y_;0Q!iJ6$RVD}%ae5OeFL|jArtM5yq>TOVCh2l>;9L)ZzW4oj z$@lg%LMY0jfm+iQp)b!H(`$Mi%3p(aV=S&W>$_KSMeesxkE90W+5%K>+NPV3s5|A1 z3<`VC^jy{2q4w<+d5Z;dY{X_VGZ^Wi<|(a1(}4997VM{w_Tx?$rYYMn$V1cTf*zZP zgqkb^o+jmK##8z{F+Jmd`c7LIi&Og9#rmD9G!E9^{KON@FShvB`V(RNJpu7I`IyA} zgrz%heu;YRLh30Kx-}nFA2N;DV`j7&tOaSq!leBu!FD>?p7S#e;m;^eE9k1QD@%ze vIB;HKrho~-WQMt7s1`RF%tgj{#>8?VxI~4#EN?~5r4qU3s$4KmXNTDjA#=WM diff --git a/.sqlx/query-90ed2cd2f8161552dae06ab2d9cfee8914e253db00e6a648a78e99ade6a4de60.json b/.sqlx/query-90ed2cd2f8161552dae06ab2d9cfee8914e253db00e6a648a78e99ade6a4de60.json deleted file mode 100644 index 9be1170ab262651ea6e31d8f5b373f4494dc970a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 253 zcmXZV!D_=W3Iu~6$GV?bJtRKO9M1_?ZtcDtyfp7`5INv^gzs0^IOqwUVR=szk8SLI zYPra^?6<#RJ`AJ4W}6Cyju_?8#4+hTCffA-ZW73Eq3eHkm_tAxQsHJRKQ~t$j-80I vn#r_ec6DCmWoZamSLhAdcE?=XykJu<$ZO3NRz;1EHCu}$uR?}a*){tEsUbsh diff --git a/.sqlx/query-921a0775f75d9e2f67f604265872bf715af1e1ed4d420ac6fa132a94ff352e56.json b/.sqlx/query-921a0775f75d9e2f67f604265872bf715af1e1ed4d420ac6fa132a94ff352e56.json deleted file mode 100644 index a7c8c1e400939f6a9c2be4bf5295272cf5080078..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 369 zcmX|+!E3`H7{%}TE5!2-(Z;w8#ttK8gO0Imu)`?D#Lq^srZeVHO8)n$N#{Y~eeb>R z=QTqJ%l2T!3Gf5LpC3Ez0pCUWcLH}wV!f-MzYsy&=KT$IgY|AO2Hc3gU+Shtla}Za zJuN8-W9M`m+peJ{^mK!nPIe5!PZH2NNWC;|jzAq$#|u2h0-sHh61HjDx*aW-dvT})em diff --git a/.sqlx/query-96a0b9960daa8a10e04e22cba592870941545b095193612956c29d37a5a1b774.json b/.sqlx/query-96a0b9960daa8a10e04e22cba592870941545b095193612956c29d37a5a1b774.json deleted file mode 100644 index c86b729bb8f9ad8a260bf9108b4a263bb937a95c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 362 zcmXv}O>4s-7{2FMi02(NjY%Dh9Y)3m9b?&GhcU`)w2fd*r{+*f{`=IV9`xmTpD(Wk zLRgBug_FYf2!DR;bWr#%!GDu-mkhSM_UQ{z^xVC_p#VpZzF*p|O(S|hj|*KY-#aam z%{9uSK|h-5WPJpFGJw`aUfvRERwpPbMkloe6BQ&?SPl-Cv)XHW7*m;78}PcC^ZUCn z@flqFL{KgTE)Ak%r;kI30L|Qa<=V*@5GHlKRXc$3YB7v=CgK77kTQ7IGs-tLEjg?ElFLm445(ft!rACi2r>_+Asyj3?7nVJo=tF~a3=Db+CM`tq%-(#ZjtT>emyw(OZ_6a9nDAAiubhEi)^4aYj8utsrW1!4G`Pg z4-uEGl$1%cLAS6il?t5eKR=zXzO5cEs4C39;&4l*EDD3E9|k}J$^w5f);G^7kMc32 cIgfY@GXi9$K@z|$h#6;*Pr{f^@ZdD~0lxmN7ytkO diff --git a/.sqlx/query-a0c9c1a108c6560b4f073c866415a94af2e823a8d88ab2aa5ace8c2b56023004.json b/.sqlx/query-a0c9c1a108c6560b4f073c866415a94af2e823a8d88ab2aa5ace8c2b56023004.json deleted file mode 100644 index ead136f579236f7ae78851cadb9ebddd043b85b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 811 zcmbtSO-sZu6uj@R2sw&}^kZvQ5D!vOSP@sj!y@H1No_Dq*R(~-vj5#AZEh~8+e4u9 z-pqT+%zl9o=4GLw5%?0}=cl#o1wME1=OE1J!F-+Hyr465yLo&-4KULdz&HQPTKJfL6quIrKYb$kM2Pw+;**S!TI-#dGT^`I1#$^|CmqXN~5F!nD$ z(-QXd;<$#cc3A@ J5F4|@;v0S`!DRpd diff --git a/.sqlx/query-a1f5699889cfabb3f681e6bb71a8d7c739d0cb82eea1f8289d012c37bd30c776.json b/.sqlx/query-a1f5699889cfabb3f681e6bb71a8d7c739d0cb82eea1f8289d012c37bd30c776.json deleted file mode 100644 index 2dfa51693c4610fdb95834a564a60d716133f3e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 255 zcmX|)J!``-5I}eT3Zc1!Wm8KobO_X2hvuVG2t{`~*9O^6WH}H*{(DbJ=y>nmy}M>Y zNC!Wo9Z0?jxm|zuk>pFlr-i~L!SAn+)vggZjWJB#Q<%lJ*$IoWKa+SD`NMZKW9T7u zu6)l0ocuD3GlTshB^e!U;8&3w{~FUx|Jy|d`CYm_-Er~Wf=|Kyfj4(9J3Dtwq*d)} sRoBMU*jj+Ht&E}yKr3sUMqSn}M=%8uo2D>4ssc+5uFO&EN@ln04^)Xo761SM diff --git a/.sqlx/query-a64694ec1a81472a05a68b1caf64c32a261bb7d1f34577c0f4dc52e318124ecd.json b/.sqlx/query-a64694ec1a81472a05a68b1caf64c32a261bb7d1f34577c0f4dc52e318124ecd.json deleted file mode 100644 index cd86c5fdff422df7192daca1f421505a37d2dabd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 874 zcma)4?MlNi6#c)a2>B^embUArAi}ibWNugoB2G%0q&AwRvma;~dv}v`VH;Sqg+fox z$L+b@^Z!<1JHo@)5BW<_Jba2j4MKuI!2Yr#tA1l&aP`#k_AvsoB{ zyZK}W-fKxJp3Ask0ynW0E#zgSZd5ExY(OJoLceIZ5V}J2~Hbm#FthvkFM2V zz%vzW^?*M&}G75H#>xV5Y{QuCp8uDh&}6)EzO^;qP|f1FLifysVq_W(VzUa;@# zKh|qj@SK*vcj9%q9}RA*d2Ok1jSKa6R;!APu diff --git a/.sqlx/query-a69f0bf9b3fefd7914502aa15fa120ecf4e5542011b781d8023bdb422580d97a.json b/.sqlx/query-a69f0bf9b3fefd7914502aa15fa120ecf4e5542011b781d8023bdb422580d97a.json deleted file mode 100644 index f6c5009b534f6a10a01ad93b1009d45e3b5faa60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 731 zcmbtSv2MaJ6x{g=%M(-#u}L85f;ylI38iX)E>(r>*e0%wLmg*8h<~r`kgAm!sy2fa z-@AABaxdor0Fcq0CI=4h0bDF zlWYu{vf`{&++9ZC(o8B*y&^mK(p6J~CLyePtbh1NRy!)U8s4 zD!A&wL)d%yC+?;z( z&bc{{gpgY8EF4Ha2>JQ3tQ^TZ5C3-5oC26Fv*(R)uoo}+`rQ|;SgclAF5cFQmH6o_ zx~jH2&sg8y$BgYNEgpkv%+>Za%d;EJxW$rmXJ{Zk4fdK^Y%1krojDE%!rF8P+cNMK z0C8!A=5+%03wjT-rfzg)!La7u4>c_5PDeJgMeWDi(4Ui+ULVfyfhv#C@V_`}yyp;t zpUkU$=@^Itx4&?0XACIg<6hGhU|Jd;(}_kt eRIDMLftmplrgKawB}gKq1m|%SD^zmSkNyG8LwQ;N diff --git a/.sqlx/query-a8b2d9cfd386b5f9ad5b76ef08711691dae057a431fffc27417e4f5504dcfb30.json b/.sqlx/query-a8b2d9cfd386b5f9ad5b76ef08711691dae057a431fffc27417e4f5504dcfb30.json deleted file mode 100644 index bd4f9f704b9c1f5e75ba047d127e0bc4549c1306..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmXZVy=ucS6o%p5S8>qX!HO-*E`%&q>5vfW7CM9w^m9}j6t}gkp@iIfkCLvBzVqr* z05E4xlRhJR1orm29T#K|nST<_zZsv@R=q2D?)N*0c0iW`M)-J9y@E74Si|Oi;V{~H zG`V!40yXScNaraM9F93k^sQ(5Y{5C}eMZ`kpTk1HZ=>t~c1j^=6LRoyq?ensLC1lF y>DH`VXT7W1#t7eOCyZ>+m#yTy>A2Ha3aRUgd++T^)?8xkg(z!hy{Sr8T#G+9R7C0k diff --git a/.sqlx/query-a94cb95af7e6c13e0e155c74de2febe44c1250f25989034a3f9e4e8bcb39dece.json b/.sqlx/query-a94cb95af7e6c13e0e155c74de2febe44c1250f25989034a3f9e4e8bcb39dece.json deleted file mode 100644 index b203d50a3630be5e7bf7ed06f3a1c1ddf6c0d506..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 259 zcmX}lKWoD<5QpL2pW@J*!GEMCDugUm=#WtA7CM9wq|>=JD0U;;LkaorJ*B1Vqu;%{ zW&pywQ*X~C9zndl?z)M@BJpLW_?_^{?997?ZT+$b9Qr8^Ay6FQzbLvbJ7{Q*)9747-dF9 diff --git a/.sqlx/query-ab8dafa95af67dc06074e83e1ec61be2fe713637f883d175f9b279f0f2f8fd87.json b/.sqlx/query-ab8dafa95af67dc06074e83e1ec61be2fe713637f883d175f9b279f0f2f8fd87.json deleted file mode 100644 index 24cf02ea61a9a3ae2a4e24caa5ebec68dd364bd4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 712 zcmbtRO-sZu6ukFWgdD{~KX#i|5D!vOSP`q>L8PS3i;ZT}wI4`X_P?8t=H{Yk4}s2m zGxM0)2MA$SSBh={pCSBkzY!zgQv-iY(9aeuHpS&5IzdLS3}bR zn!K`>VCWoOH~|Jj(~H^&@3o+oY&tn@)u4fA3!r6ShyDh^EZtz5()COzDm5iE zSGf<5^A0MZcxUdrQDeHo%kbd0oTb^vUilN?ozeMw`N$JB;cxOTkYne^|JL_WSH~=p z-&xqCUfeF;(O!2yLT_5}nc-5rwxb3jySrqH)g&dYr76_+$Eh5^+y3z5eD!Pfa7%~Q z)JN-#QBLzUX;eeZfka7~5T0cTi^!Vibxd*|@|b6wWdsL@;0pnj Bt*HP2 diff --git a/.sqlx/query-b41855e635ac409559fa63cba4c1285034c573b86e3193da3995606dee412153.json b/.sqlx/query-b41855e635ac409559fa63cba4c1285034c573b86e3193da3995606dee412153.json deleted file mode 100644 index 94419bd3f023b55d3557d0e840af795b0a075adb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 717 zcma)4O-lnY5WV+TgdD}g?q;)g1rgB|g{oK!9z;q$rZ$jfYc>m(;(s^EHejJ*b4bXW znfG31vK=9WC7&C%0(_3}%kxAzz^4KJ?7?m#*i4%189G6?)29byL93$+~429WV1SrBD&dM48o;)(chw8|<8 NQ07HW2_AiqegHQD@@Efr2x7S|ubmcb2xtm~V63l1uzSr}8Y`5c+F26c(fHwR z*g>U?Y@zdyZj{os3XgXBK`16zbEw4fJ+THxf6J;lt z_yYJ4bohA!k2Ap=^`IG MWSQn1@a!=A22AqBBLDyZ diff --git a/.sqlx/query-b91f6aab0bdb316633b3a0d75303086dd6e4e204e9e063535b19db666476fe88.json b/.sqlx/query-b91f6aab0bdb316633b3a0d75303086dd6e4e204e9e063535b19db666476fe88.json deleted file mode 100644 index 24b6a1344b8e12b826c4f74fdb6a004790e20adf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 900 zcmb7CU2DQH6n*zsgglN>T2rGA#+ajH4yUXNdl;i6O=>%0;@Wgf#Q(nZQwtMZ9}4%} zd(S=h=Ina_Kt=;eHXPmnxO|-@n#1b?{>pi}D?xAOJ*+?$#+>DXTUX%GU%Y@&Nu4So zc$$IxWt#@m`P}ot^I|#&pScWmqGX`S8Z5f8)$j!g1GjCvWumty&-XgbmLaI>?kaJN zXJMMqLiFBi!AZpch9j;loGA}^7tQBDcq9OSXCR(wJ&e(>L=@18^9@R>t}*8 z7lKexc%-pJL`24$ss9No5$0c#!Cbm>Wmr17`sCp@zDtO4!vLAbmDmc diff --git a/.sqlx/query-bcd9d4dd3641e6c84262ed5f6c5646f825be186da276b113c0150aaad26b057c.json b/.sqlx/query-bcd9d4dd3641e6c84262ed5f6c5646f825be186da276b113c0150aaad26b057c.json deleted file mode 100644 index b3120648fb0c5f528493a9c8d6a7f4fabc926454..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 871 zcmbtS%WA_g5WMFrgicAx!FFWD?WOb(N+7t@NqQ+I=;c~ekyAe?Cgk5M$rkkBL)#c* zw4>eGSMXcUx2tRi*6HpE90{w+17VFUbq%I{ zDES;zLXqxZPc>T|Ao9<*3>AZ9k=>{V>TwZ)u z-_qto&+O+L=MR0QsrVni|7+v?(bfDvycln>xVpvQ@HjpC9ldkntbr$Wf#WpM^02NeA~YSy$JJlv{3kpK5iJ8sRxL`7oF>zsxIsHLM0A~s;4DsIQRNX%P&f7Tx zU{+sx>>OJI``H)G=-49g-{kx~;HN6oLBY2C{(=sTzYTEQsZznzSlGbFYi`|Oe4|5V zrgI9ITRV0AKyW&TBGE5;`e}4N=xat=HrIM2;Lp+Hz3r#AMcszr=}a$AXEhEr2{Sxh wp)`3WQ7n1vq)aokX_aG^n?xItaCi>AvyDu^(tC``mT*oDYQ(!>R_WTvS&#{RoW+U^4!)LsPo zo^zh_a?aWH0DuKuYO?0=2Ee!1sdOA(2l=bwb{oZfn%~dC6?mAvJOgI5vqmXyOR#v% zXE~4sxCM80QNc@QB@M8hhj^ocR*;6D!kqfUhzV*Lp@e-ww!`?=2K=gN5j z#oT61P*+M3s=|%5Cn1O`dE50rKeY+}MmSuNauv>l^%FE03W`uF7)!?@84F4|OEAht f3?r6_5fVd!Nnc=`rbrBY5Tz{3I28kbS^M5ErJTKG diff --git a/.sqlx/query-c9437ff0c3014b269dcb21304fbad12237b9cb69ea6aa4686df6d5262065faa2.json b/.sqlx/query-c9437ff0c3014b269dcb21304fbad12237b9cb69ea6aa4686df6d5262065faa2.json deleted file mode 100644 index 10fe1a36ac951c19efe15b9d67c9523383221b7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 702 zcma)(UrWO<6vf~7DMB8_hc@jpD~JzLQJ9Ez;DgA>Ul+*InWo?v`|c*`0v0B=4=p|S z-ru?0bC>}DIb9pF74RCs*O#So0$v9Bs~2`3#d=xat-uAiZ=Rok6`Jfj@b*wQHDIlA zw$)nLwc_9g+zz8$bj&ImkOzj9#)>R2DZ&+J6y-%$N=kT+P_dvDqqq_T6H?+U%q8Y?gi(g)k`M%E$Jq}> CL8-9- diff --git a/.sqlx/query-cb0d76a3a1cfa439d48056c865cd5bfbfbc785fa795254120822b843ce63ff07.json b/.sqlx/query-cb0d76a3a1cfa439d48056c865cd5bfbfbc785fa795254120822b843ce63ff07.json deleted file mode 100644 index 0626f8d69986d8eeb209fa66ec8dc1b97146e5ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 252 zcmXZVJqyAx5C-7AzanIIXw#;Rg18uQP{b-Yh{)y37HZXg3{v{vy{Oz0p1k+o5D-F* zy4G;FbVlf5zbQLQC!SxuZQdT2awCtDtn%HKbZ~3QX)SZb5eo%lN+hwiY3y8>(O?WdK9NFi diff --git a/.sqlx/query-cbda6fd5137f3698537262772df22a0a66064e3416e4f5c89d8d83fa6c0536ad.json b/.sqlx/query-cbda6fd5137f3698537262772df22a0a66064e3416e4f5c89d8d83fa6c0536ad.json deleted file mode 100644 index fb83516f9c79c40890bf91f2363ec3b4d1cf5c32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 367 zcmXv}O>2WN7{2=}Le4v=(P#%_hvC?uW6TY97^S=>Z)PBkozX)n{`V!(=8(Yioohk} zmEF<83F$qdpC5ZYA-xOm-;CoWg3Z2p`XU*5Zr|UC6Fth=pd;TeP1}%JOY%S-SC2#| zM%{(1tIzN$VleZ`IuHCrfNVr0Z?#!;2go~yQECguJJPUYJ#@O9@u;mD!lG9@(T!T- zdsvn945R*=A=Ina_K$5Qn+EDli;Nor06orm~ziV3V3>YViS(<@3$zB1LLY0zpT2`Q6GvWX< z>VT4Rv|C|W3pD`m&-3MM0s2o4Fz*1Qvuv3raqT(aC~?zNUy!;;q)W3dAegss88Cn9yHK4%et! zn|NS?5%tGmRE+a*IP$&Ua4 diff --git a/.sqlx/query-db48fb93e9f22ee7da0786ae913d962b1da35b5b2b663f759f6af106650951b8.json b/.sqlx/query-db48fb93e9f22ee7da0786ae913d962b1da35b5b2b663f759f6af106650951b8.json deleted file mode 100644 index f31d95b29c414b84f8010a3cd632f23106a97f89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 467 zcmZutO-sWt7{2#cg!d>O`mv^sAReTmFcIs(gUCphWDR8LY*TQI{dbeJ5d?Dx#xe@*nGs__n{!A>nhT!jTT{gRB_*HZcoYwePG0vcO%sfSI(W zO#6?BdrPWE5mUosc6WDBK}W~VFUQ%cmW&jF$UGOK;xw&lOa;jbPH@gLPK%P2EN2)O UvMPzhRAwp0WlGA7)sS409{@XWW&i*H diff --git a/.sqlx/query-e9386ab7ecbe4ce13f8ee5ee5852697741fb210ea8bfc3d61f1508106c0b076e.json b/.sqlx/query-e9386ab7ecbe4ce13f8ee5ee5852697741fb210ea8bfc3d61f1508106c0b076e.json deleted file mode 100644 index e54e4a6d57525665b624da7938dc2ca3166e54a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 684 zcma)(TT8=05QX3KE0%o}AF@evsUU(9QK*Qu;Dbon%fu{flbB7wQu^QB-DI(aiVY!< zGjqN<%W|#e+|%Womec2yA`@X_si!eq@=AC4gwKEJu;f)$LwKl@L6VOy}ML$*wXSbTMG@Xz&RmKw9AmfAkZ3}Bz zmyM0|Soi4+PsP3O8b{iG?8zqGX*BUB(|m-!RSi3b>?RyQep5#W>JfSc-~Bo07CPAo zeL8sj7yT%>Tg}JVid)*5`}WY;Gx#OprTnaVBM{d+EfLp)Qj{rsL4UB4Dh+<>KR@GK z^L0F2QC-=4!931W$}->#W*kUd<^YL#^CU{=SvDgji%1AGXC;qCN=lN1ISEr9vM@^l H2m9a$wUDC^ diff --git a/.sqlx/query-e94ef5bc8b267d493375bb371dcfb7b09f6355ecbc8b6e1085d5f2f9a08cac3f.json b/.sqlx/query-e94ef5bc8b267d493375bb371dcfb7b09f6355ecbc8b6e1085d5f2f9a08cac3f.json deleted file mode 100644 index 391ca72c3136de054678926d3710228e68bcc047..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 783 zcma)4T}#6-6n)>X2zeA4>$# zo_p@ey*&;9fF&*@+EI7|;OceGHHFs}{%WW?I53-M4{M;PK!@QKc+8hCKB&R{%Ade& zvB+}pyqqn-XCsN`l`J&cfMq`(I+22x;I`E>$G4{}&w6D;Q|d`~-b+bqqByo5kH(vE zM=H_mWNmB=o;N-9-yiG(Q`*$esy>cgHET(ZhubtZK@fo<~Cv3yI* zhn=$fcKM!m@C)r}uj#kGNV?f!s?Hl9|0RC0aZ*XGDj}#c13up*ysU|v(ue25JyPZu z(p1@vbn}8a-)yx30>^FV31uS$!on`I+Z{uqrd`{A0^irXogUnxdTUol(+On}CR03) slX8-VVG>~+CP7IUPVgvYaTrCESVl~P(KIR}HfAXrO$j0)gM-uH2Z9C0`~Uy| diff --git a/.sqlx/query-ea4b06aaad9436096e20a53d81fd36ed21da18ceed5b71e51a1e749bab466422.json b/.sqlx/query-ea4b06aaad9436096e20a53d81fd36ed21da18ceed5b71e51a1e749bab466422.json deleted file mode 100644 index 1967a26f3542406ea6db26d84841f21bcaa2ebb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 481 zcmZut&uhXk6u$dcgq(Ej(9{_1E@Ou>3evG!y^J9*=2Z(db!~)+_`fen&0w$`9^`x9 zkN4hnL(cksD}u z)Lc%ut##G<(Xd9b hm6{4vF;l=6J47{;bA~ep4Cg$NAh{4cj_K$&`T=i6bj<(& diff --git a/.sqlx/query-ee7da56331bece2efe21b55dbd5f420d3abb08358a1abe301dc7e08693fbef4d.json b/.sqlx/query-ee7da56331bece2efe21b55dbd5f420d3abb08358a1abe301dc7e08693fbef4d.json deleted file mode 100644 index c4ec08d2018529aad439bf793baad37af1945866..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 678 zcma)3!D@ss6ukQtA;;2#8g*^2WebHZrQ5xgB8hpaLX6uOp|b41H;D$i(4~4%ocCtt zF>{I#!kkr#c7X2@etX@u2Jo$ezedoL2e-TJ!vWo($NkGQ(je(H%qtv*TDQzmPnKdy zbuY(G4HkI!09poq7+hNJ=?=4$Uh`H_X(_SuogZwbBUG&ty}RH^nIgl>@Eo>gOSg}M zvj2wpg?91_z0f|uw^=1Ub`132ho{%XS07&Y%IIE7(ABkc$T}0B(#O1uk5t=c4QSU$ z$P4e=&&IgC{qB+{R-=@ZNmpj*PEa{Ou>I#}_O)-XhZ{OHt~!7Xr+Gn0!k~r}1f&Ve kbIy1!vNYy|G8UI4FKI#2I8`~BZ7g*IF8QI4{B$l761SM diff --git a/.sqlx/query-eee8820f59927c1560a5fd461b9f4a2d40abdf3843d0c815399ea9d267cade5a.json b/.sqlx/query-eee8820f59927c1560a5fd461b9f4a2d40abdf3843d0c815399ea9d267cade5a.json deleted file mode 100644 index cb7c1bdf5ea441776a329bb9538c6ce8903023c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 574 zcma)&PfNrw5XJBNDMF6oq5roPL{KUUD`H*nAhJxGsSTv*nzrDw>~}Xw8{I_@<`Br6 z_kJ^({elq6%8i5;=>?(BPb)qkJvZ{lh-zoWe3jp<$r-sV9v?^xmFjx(a+eo5895`D z?_4f1KMkVMpT^`TiI*j)e2-T*rMynnT;DwM>@s01fe6InPNJ0*llscW!;&1 z?&Xj~bS@s8Yieoy)RFacf1`aD(&iKNwdl(KkZp$}$j{Wlk$Qq&~}BBp8J)p3>u s%gT=v58|3diC<=Q1ObaYC|Mn*QOLY1_2V$h5Hc1d%L*9AKo^I_7ft+$N&o-= diff --git a/.sqlx/query-f75318134ec1facc3de6b04232cb28a92524b8f556840b76f76c8f317059e668.json b/.sqlx/query-f75318134ec1facc3de6b04232cb28a92524b8f556840b76f76c8f317059e668.json deleted file mode 100644 index 88dc7d1903e253064c928e6ec7af5fd162667334..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 311 zcmXxeOG^VW6b0b@{EAD~UCblNq=JaxfD0GeR$P=aiN7c8=Y+4%5BIwUKBk~YcM5HfUe4{HJ2aC=*fu-(LHsmw zhFiE>!IC?(y#9NwGckp?$7a*O5-vLi2i-fB_Rn!QF6)z@j$w$fKcrJ4eaSwaIAWi4 zsYTtm_il_pucgahH%#73<1F+rO%aANvWd5FQykvZTT|e(T&|^q(^xnn^cHJKG4m`rs0;2VJIq7i@ zDFb|Lx@`x8$&cTbv6GI2$~Ugmrn(;ed=3-A;h4`6ePN_zqW=FF>2~<+6G43+y8X?D z*?Uwz2MPeWBs^utEv@H)aDgLH=kuGXu&Ew}>ro2N(6QtrBbqzKt_r&*L9gCvM} zXNK1H0eDRSmQ?y9(hy_XKgtpWK35vPnf&Q?UJ8e}LH+ZnVT*7I1%B(Rm@?mx! z{<1EzD3C9#Z%xPkVo~`UaHHQFA8Sb5 z>6_3F#!zNr$GK8PO)qom@5dMw?qmTQ+BdO8B_tA@7L-xUmNlZa!m325ED2$fU_=Sy a3X7tsaJ|5?T2x_F@Uj-1%n5?C)9eczsf92A diff --git a/Cargo.toml b/Cargo.toml index 92bf0a0..db76e00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,31 @@ -[workspace] -resolver = "2" -members = [ - "emgauwa-core", - "emgauwa-controller", - "emgauwa-common", -] +[package] +name = "emgauwa-core" +version = "0.5.0" +edition = "2021" +authors = ["Tobias Reisinger "] + +[dependencies] +emgauwa-common = { git = "https://git.serguzim.me/emgauwa/common.git" } + +actix = "0.13" +actix-web = "4.4" +actix-web-actors = "4.2" +actix-cors = "0.7" + +utoipa = "4.2" +utoipa-swagger-ui = { version = "6.0", features = ["actix-web", "debug-embed"] } + +log = "0.4" + +chrono = { version = "0.4", features = ["serde"] } +uuid = { version = "1.5", features = ["serde", "v4"] } +itertools = "0.12" + +serde = "1.0" +serde_json = "1.0" +serde_derive = "1.0" + +sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] } + +futures = "0.3" +tokio = { version = "1.36", features = ["rt", "rt-multi-thread"] } diff --git a/Makefile b/Makefile index 7c49652..8d69e1e 100644 --- a/Makefile +++ b/Makefile @@ -1,42 +1,23 @@ -sqlx: - cargo sqlx database drop -y - cargo sqlx database create - cargo sqlx migrate run - cargo sqlx prepare --workspace - build-rpi: cross build --target arm-unknown-linux-gnueabihf -emgauwa-%.json: config/%.pkl +emgauwa-core.json: core.pkl pkl eval -f json -o $@ $< -configs: +config: $(MAKE) emgauwa-core.json - $(MAKE) emgauwa-controller.json clean: - rm -f emgauwa-controller.json - rm -f emgauwa-controller.sqlite rm -f emgauwa-core.json rm -f emgauwa-core.sqlite - rm -f emgauwa-dev.sqlite - -emgauwa-controller_%: - $(TOOL) build --target $* --release --bin emgauwa-controller - mkdir -p out/releases - cp target/$*/release/emgauwa-controller out/releases/emgauwa-controller_$* emgauwa-core_%: $(TOOL) build --target $* --release --bin emgauwa-core mkdir -p out/releases cp target/$*/release/emgauwa-core out/releases/emgauwa-core_$* -emgauwa_%: - $(MAKE) emgauwa-controller_$* - $(MAKE) emgauwa-core_$* - releases: - $(MAKE) TOOL=cross emgauwa_arm-unknown-linux-gnueabihf - $(MAKE) TOOL=cargo emgauwa_x86_64-unknown-linux-gnu - $(MAKE) TOOL=cross emgauwa_x86_64-unknown-linux-musl + $(MAKE) TOOL=cross emgauwa-core_arm-unknown-linux-gnueabihf + $(MAKE) TOOL=cargo emgauwa-core_x86_64-unknown-linux-gnu + $(MAKE) TOOL=cross emgauwa-core_x86_64-unknown-linux-musl diff --git a/emgauwa-core/build.rs b/build.rs similarity index 72% rename from emgauwa-core/build.rs rename to build.rs index 27e067f..cfdf256 100644 --- a/emgauwa-core/build.rs +++ b/build.rs @@ -1,10 +1,10 @@ use std::process::{exit, Command}; fn main() { - println!("cargo:rerun-if-changed=../api.v1.yaml"); + println!("cargo:rerun-if-changed=./api.v1.yaml"); let output = Command::new("sh") .arg("-c") - .arg("yq . < ../api.v1.yaml > $OUT_DIR/api.v1.json") + .arg("yq . < ./api.v1.yaml > $OUT_DIR/api.v1.json") .output() .expect("Failed to convert api documentation to json"); diff --git a/config/controller.pkl b/config/controller.pkl deleted file mode 100644 index 11e735e..0000000 --- a/config/controller.pkl +++ /dev/null @@ -1,56 +0,0 @@ -amends "package://emgauwa.app/pkl/emgauwa@0.1.0#/controller.pkl" - -relays { - new { - driver = "null" - pin = 0 - inverted = true - } - new { - driver = "null" - pin = 1 - inverted = true - } - new { - driver = "null" - pin = 2 - inverted = true - } - new { - driver = "null" - pin = 3 - inverted = true - } - new { - driver = "null" - pin = 4 - inverted = true - } - new { - driver = "null" - pin = 5 - inverted = true - } - new { - driver = "null" - pin = 10 - inverted = true - pulse = 10 - } - new { - driver = "null" - pin = 11 - inverted = true - pulse = 10 - } - new { - driver = "null" - pin = 20 - inverted = false - } - new { - driver = "null" - pin = 21 - inverted = false - } -} \ No newline at end of file diff --git a/config/core.pkl b/core.pkl similarity index 100% rename from config/core.pkl rename to core.pkl diff --git a/emgauwa-common/Cargo.toml b/emgauwa-common/Cargo.toml deleted file mode 100644 index e11518a..0000000 --- a/emgauwa-common/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "emgauwa-common" -version = "0.5.0" -edition = "2021" -authors = ["Tobias Reisinger "] - - -[dependencies] -actix = "0.13" -actix-web = "4.4" -actix-web-actors = "4.2" - -serde = "1.0" -serde_json = "1.0" -serde_derive = "1.0" - -simple_logger = "4.2" -log = "0.4" - -config = "0.13" - -chrono = { version = "0.4", features = ["serde"] } - -sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] } -libsqlite3-sys = { version = "*", features = ["bundled"] } -uuid = "1.6" -futures = "0.3" -libc = "0.2" - -rppal = "0.17" -rppal-pfd = "0.0.5" -rppal-mcp23s17 = "0.0.3" diff --git a/emgauwa-common/build.rs b/emgauwa-common/build.rs deleted file mode 100644 index 827c260..0000000 --- a/emgauwa-common/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=migrations"); -} diff --git a/emgauwa-common/src/constants.rs b/emgauwa-common/src/constants.rs deleted file mode 100644 index 92874a2..0000000 --- a/emgauwa-common/src/constants.rs +++ /dev/null @@ -1,10 +0,0 @@ -use std::time::Duration; - -pub const DEFAULT_PORT: u16 = 4419; -pub const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); -pub const HEARTBEAT_TIMEOUT: Duration = Duration::from_secs(15); - -pub const WEBSOCKET_RETRY_TIMEOUT: Duration = Duration::from_secs(5); -pub const RELAYS_RETRY_TIMEOUT: Duration = Duration::from_secs(5); - -pub const RELAY_PULSE_DURATION: u64 = 3; diff --git a/emgauwa-common/src/db/controllers.rs b/emgauwa-common/src/db/controllers.rs deleted file mode 100644 index bf0a1f0..0000000 --- a/emgauwa-common/src/db/controllers.rs +++ /dev/null @@ -1,184 +0,0 @@ -use std::ops::DerefMut; - -use serde_derive::{Deserialize, Serialize}; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::{DbRelay, DbTag}; -use crate::errors::DatabaseError; -use crate::types::EmgauwaUid; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DbController { - #[serde(skip)] - pub id: i64, - #[serde(rename = "id")] - pub uid: EmgauwaUid, - pub name: String, - pub relay_count: i64, - pub active: bool, -} - -impl DbController { - pub async fn get_all( - conn: &mut PoolConnection, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbController, "SELECT * FROM controllers") - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get( - conn: &mut PoolConnection, - id: i64, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbController, "SELECT * FROM controllers WHERE id = ?", id) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_by_uid( - conn: &mut PoolConnection, - filter_uid: &EmgauwaUid, - ) -> Result, DatabaseError> { - sqlx::query_as!( - DbController, - "SELECT * FROM controllers WHERE uid = ?", - filter_uid - ) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_by_uid_or_create( - conn: &mut PoolConnection, - uid: &EmgauwaUid, - new_name: &str, - new_relay_count: i64, - ) -> Result { - match DbController::get_by_uid(conn, uid).await? { - Some(tag) => Ok(tag), - None => DbController::create(conn, uid, new_name, new_relay_count).await, - } - } - - pub async fn get_by_tag( - conn: &mut PoolConnection, - tag: &DbTag, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbController, "SELECT schedule.* FROM controllers AS schedule INNER JOIN junction_tag ON junction_tag.schedule_id = schedule.id WHERE junction_tag.tag_id = ?", tag.id) - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn delete_by_uid( - conn: &mut PoolConnection, - filter_uid: EmgauwaUid, - ) -> Result<(), DatabaseError> { - if sqlx::query_scalar!("SELECT 1 FROM controllers WHERE uid = ?", filter_uid) - .fetch_optional(conn.deref_mut()) - .await? - .is_none() - { - return Err(DatabaseError::NotFound); - } - - sqlx::query!("DELETE FROM controllers WHERE uid = ?", filter_uid) - .execute(conn.deref_mut()) - .await - .map(|res| match res.rows_affected() { - 0 => Err(DatabaseError::DeleteError), - _ => Ok(()), - })? - } - - pub async fn create( - conn: &mut PoolConnection, - new_uid: &EmgauwaUid, - new_name: &str, - new_relay_count: i64, - ) -> Result { - sqlx::query_as!( - DbController, - "INSERT INTO controllers (uid, name, relay_count) VALUES (?, ?, ?) RETURNING *", - new_uid, - new_name, - new_relay_count, - ) - .fetch_optional(conn.deref_mut()) - .await? - .ok_or(DatabaseError::InsertGetError) - } - - pub async fn update( - &self, - conn: &mut PoolConnection, - new_name: &str, - new_relay_count: i64, - ) -> Result { - sqlx::query!( - "UPDATE controllers SET name = ?, relay_count = ? WHERE id = ?", - new_name, - new_relay_count, - self.id, - ) - .execute(conn.deref_mut()) - .await?; - - Self::get(conn, self.id) - .await? - .ok_or(DatabaseError::UpdateGetError) - } - - pub async fn update_active( - &self, - conn: &mut PoolConnection, - new_active: bool, - ) -> Result { - sqlx::query!( - "UPDATE controllers SET active = ? WHERE id = ?", - new_active, - self.id, - ) - .execute(conn.deref_mut()) - .await?; - - Self::get(conn, self.id) - .await? - .ok_or(DatabaseError::UpdateGetError) - } - - pub async fn get_relays( - &self, - conn: &mut PoolConnection, - ) -> Result, DatabaseError> { - sqlx::query_as!( - DbRelay, - "SELECT * FROM relays WHERE controller_id = ?", - self.id - ) - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn all_inactive(conn: &mut PoolConnection) -> Result<(), DatabaseError> { - sqlx::query!("UPDATE controllers SET active = 0") - .execute(conn.deref_mut()) - .await?; - Ok(()) - } - - pub async fn reload( - &self, - conn: &mut PoolConnection, - ) -> Result { - Self::get(conn, self.id) - .await? - .ok_or(DatabaseError::NotFound) - } -} diff --git a/emgauwa-common/src/db/junction_relay_schedule.rs b/emgauwa-common/src/db/junction_relay_schedule.rs deleted file mode 100644 index f081a54..0000000 --- a/emgauwa-common/src/db/junction_relay_schedule.rs +++ /dev/null @@ -1,146 +0,0 @@ -use std::ops::DerefMut; - -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::{DbRelay, DbSchedule}; -use crate::errors::DatabaseError; -use crate::types::Weekday; - -pub struct DbJunctionRelaySchedule { - pub id: i64, - pub weekday: Weekday, - pub relay_id: i64, - pub schedule_id: i64, -} - -impl DbJunctionRelaySchedule { - pub async fn get( - conn: &mut PoolConnection, - id: i64, - ) -> Result, DatabaseError> { - sqlx::query_as!( - DbJunctionRelaySchedule, - "SELECT * FROM junction_relay_schedule WHERE id = ?", - id - ) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_junction_by_relay_and_weekday( - conn: &mut PoolConnection, - relay: &DbRelay, - weekday: Weekday, - ) -> Result, DatabaseError> { - sqlx::query_as!( - DbJunctionRelaySchedule, - "SELECT * FROM junction_relay_schedule WHERE relay_id = ? AND weekday = ?", - relay.id, - weekday - ) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_relays( - conn: &mut PoolConnection, - schedule: &DbSchedule, - ) -> Result, DatabaseError> { - sqlx::query_as!( - DbRelay, - r#"SELECT relays.* FROM relays INNER JOIN junction_relay_schedule - ON junction_relay_schedule.relay_id = relays.id - WHERE junction_relay_schedule.schedule_id = ? - ORDER BY junction_relay_schedule.weekday"#, - schedule.id - ) - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_schedule( - conn: &mut PoolConnection, - relay: &DbRelay, - weekday: Weekday, - ) -> Result, DatabaseError> { - sqlx::query_as!( - DbSchedule, - r#"SELECT schedules.* FROM schedules INNER JOIN junction_relay_schedule - ON junction_relay_schedule.schedule_id = schedules.id - WHERE junction_relay_schedule.relay_id = ? AND junction_relay_schedule.weekday = ?"#, - relay.id, - weekday - ) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_schedules( - conn: &mut PoolConnection, - relay: &DbRelay, - ) -> Result, DatabaseError> { - sqlx::query_as!( - DbSchedule, - r#"SELECT schedules.* FROM schedules INNER JOIN junction_relay_schedule - ON junction_relay_schedule.schedule_id = schedules.id - WHERE junction_relay_schedule.relay_id = ? - ORDER BY junction_relay_schedule.weekday"#, - relay.id - ) - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn set_schedule( - conn: &mut PoolConnection, - relay: &DbRelay, - schedule: &DbSchedule, - weekday: Weekday, - ) -> Result { - match Self::get_junction_by_relay_and_weekday(conn, relay, weekday).await? { - None => sqlx::query_as!( - DbJunctionRelaySchedule, - "INSERT INTO junction_relay_schedule (weekday, relay_id, schedule_id) VALUES (?, ?, ?) RETURNING *", - weekday, - relay.id, - schedule.id - ) - .fetch_optional(conn.deref_mut()) - .await? - .ok_or(DatabaseError::InsertGetError), - - Some(junction) => { - sqlx::query!( - "UPDATE junction_relay_schedule SET weekday = ?, relay_id = ?, schedule_id= ? WHERE id = ?", - weekday, - relay.id, - schedule.id, - junction.id - ) - .execute(conn.deref_mut()) - .await?; - - Self::get(conn, junction.id) - .await? - .ok_or(DatabaseError::UpdateGetError) - } - } - } - - pub async fn set_schedules( - conn: &mut PoolConnection, - relay: &DbRelay, - schedules: Vec<&DbSchedule>, - ) -> Result<(), DatabaseError> { - for (weekday, schedule) in schedules.iter().enumerate() { - Self::set_schedule(conn, relay, schedule, weekday as Weekday).await?; - } - Ok(()) - } -} diff --git a/emgauwa-common/src/db/junction_tag.rs b/emgauwa-common/src/db/junction_tag.rs deleted file mode 100644 index f1b8816..0000000 --- a/emgauwa-common/src/db/junction_tag.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::ops::DerefMut; - -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::{DbRelay, DbSchedule, DbTag}; -use crate::errors::DatabaseError; - -pub struct DbJunctionTag { - pub id: i64, - pub tag_id: i64, - pub relay_id: Option, - pub schedule_id: Option, -} - -impl DbJunctionTag { - pub async fn link_relay( - conn: &mut PoolConnection, - tag: &DbTag, - relay: &DbRelay, - ) -> Result { - sqlx::query_as!( - DbJunctionTag, - "INSERT INTO junction_tag (tag_id, relay_id) VALUES (?, ?) RETURNING *", - tag.id, - relay.id - ) - .fetch_optional(conn.deref_mut()) - .await? - .ok_or(DatabaseError::InsertGetError) - } - - pub async fn link_schedule( - conn: &mut PoolConnection, - tag: &DbTag, - schedule: &DbSchedule, - ) -> Result { - sqlx::query_as!( - DbJunctionTag, - "INSERT INTO junction_tag (tag_id, schedule_id) VALUES (?, ?) RETURNING *", - tag.id, - schedule.id - ) - .fetch_optional(conn.deref_mut()) - .await? - .ok_or(DatabaseError::InsertGetError) - } -} diff --git a/emgauwa-common/src/db/macro.rs b/emgauwa-common/src/db/macro.rs deleted file mode 100644 index 1ced624..0000000 --- a/emgauwa-common/src/db/macro.rs +++ /dev/null @@ -1,166 +0,0 @@ -use std::ops::DerefMut; - -use serde_derive::{Deserialize, Serialize}; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::{DbController, DbMacroAction, DbRelay, DbSchedule}; -use crate::errors::DatabaseError; -use crate::types::{EmgauwaUid, RequestMacroAction}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DbMacro { - #[serde(skip)] - pub id: i64, - #[serde(rename = "id")] - pub uid: EmgauwaUid, - pub name: String, -} - - -impl DbMacro { - pub async fn get_all(conn: &mut PoolConnection) -> Result, DatabaseError> { - sqlx::query_as!(DbMacro, "SELECT * FROM macros") - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get( - conn: &mut PoolConnection, - id: i64, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbMacro, "SELECT * FROM macros WHERE id = ?", id) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_by_uid( - conn: &mut PoolConnection, - filter_uid: &EmgauwaUid, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbMacro, "SELECT * FROM macros WHERE uid = ?", filter_uid) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn create( - conn: &mut PoolConnection, - new_uid: EmgauwaUid, - new_name: &str, - ) -> Result { - sqlx::query_as!( - DbMacro, - "INSERT INTO macros (uid, name) VALUES (?, ?) RETURNING *", - new_uid, - new_name - ) - .fetch_optional(conn.deref_mut()) - .await? - .ok_or(DatabaseError::InsertGetError) - } - - pub async fn delete(&self, conn: &mut PoolConnection) -> Result<(), DatabaseError> { - sqlx::query!("DELETE FROM macros WHERE id = ?", self.id) - .execute(conn.deref_mut()) - .await - .map(|res| match res.rows_affected() { - 0 => Err(DatabaseError::DeleteError), - _ => Ok(()), - })? - } - - pub async fn delete_by_uid( - conn: &mut PoolConnection, - filter_uid: EmgauwaUid, - ) -> Result<(), DatabaseError> { - if sqlx::query_scalar!("SELECT 1 FROM macros WHERE uid = ?", filter_uid) - .fetch_optional(conn.deref_mut()) - .await? - .is_none() - { - return Err(DatabaseError::NotFound); - } - - sqlx::query!("DELETE FROM macros WHERE uid = ?", filter_uid) - .execute(conn.deref_mut()) - .await - .map(|res| match res.rows_affected() { - 0 => Err(DatabaseError::DeleteError), - _ => Ok(()), - })? - } - - pub async fn update( - &self, - conn: &mut PoolConnection, - new_name: &str, - ) -> Result { - sqlx::query!("UPDATE relays SET name = ? WHERE id = ?", new_name, self.id,) - .execute(conn.deref_mut()) - .await?; - - DbMacro::get(conn, self.id) - .await? - .ok_or(DatabaseError::UpdateGetError) - } - - pub async fn set_actions( - &self, - conn: &mut PoolConnection, - new_actions: &[RequestMacroAction], - ) -> Result<(), DatabaseError> { - sqlx::query!("DELETE FROM macro_actions WHERE macro_id = ?", self.id) - .execute(conn.deref_mut()) - .await?; - - for new_action in new_actions { - let controller = DbController::get_by_uid(conn, &new_action.relay.controller_id) - .await? - .ok_or(DatabaseError::NotFound)?; - let relay = - DbRelay::get_by_controller_and_num(conn, &controller, new_action.relay.number) - .await? - .ok_or(DatabaseError::NotFound)?; - - let schedule = DbSchedule::get_by_uid(conn, &new_action.schedule.id) - .await? - .ok_or(DatabaseError::NotFound)?; - - DbMacroAction::create(conn, self, &relay, &schedule, new_action.weekday).await?; - } - Ok(()) - } - - pub async fn get_actions( - &self, - conn: &mut PoolConnection, - ) -> Result, DatabaseError> { - sqlx::query_as!( - DbMacroAction, - "SELECT * FROM macro_actions WHERE macro_id = ?", - self.id - ) - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_actions_weekday( - &self, - conn: &mut PoolConnection, - weekday: i64, - ) -> Result, DatabaseError> { - sqlx::query_as!( - DbMacroAction, - "SELECT * FROM macro_actions WHERE macro_id = ? AND weekday = ?", - self.id, - weekday - ) - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } -} diff --git a/emgauwa-common/src/db/macro_action.rs b/emgauwa-common/src/db/macro_action.rs deleted file mode 100644 index 4a88c39..0000000 --- a/emgauwa-common/src/db/macro_action.rs +++ /dev/null @@ -1,99 +0,0 @@ -use std::ops::DerefMut; - -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::{DbMacro, DbRelay, DbSchedule}; -use crate::errors::DatabaseError; - -#[derive(Debug, Clone)] -pub struct DbMacroAction { - pub id: i64, - pub macro_id: i64, - pub relay_id: i64, - pub schedule_id: i64, - pub weekday: i64, // should be u8, but sqlite will store it as i64 -} - - -impl DbMacroAction { - pub async fn get_all( - conn: &mut PoolConnection, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbMacroAction, "SELECT * FROM macro_actions") - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get( - conn: &mut PoolConnection, - id: i64, - ) -> Result, DatabaseError> { - sqlx::query_as!( - DbMacroAction, - "SELECT * FROM macro_actions WHERE id = ?", - id - ) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn create( - conn: &mut PoolConnection, - new_macro: &DbMacro, - new_relay: &DbRelay, - new_schedule: &DbSchedule, - new_weekday: i64, - ) -> Result { - sqlx::query_as!( - DbMacroAction, - "INSERT INTO macro_actions (macro_id, relay_id, schedule_id, weekday) VALUES (?, ?, ?, ?) RETURNING *", - new_macro.id, - new_relay.id, - new_schedule.id, - new_weekday - ) - .fetch_optional(conn.deref_mut()) - .await? - .ok_or(DatabaseError::InsertGetError) - } - - pub async fn delete(&self, conn: &mut PoolConnection) -> Result<(), DatabaseError> { - sqlx::query!("DELETE FROM macro_actions WHERE id = ?", self.id) - .execute(conn.deref_mut()) - .await - .map(|res| match res.rows_affected() { - 0 => Err(DatabaseError::DeleteError), - _ => Ok(()), - })? - } - - pub async fn get_relay( - &self, - conn: &mut PoolConnection, - ) -> Result { - DbRelay::get(conn, self.relay_id) - .await? - .ok_or(DatabaseError::NotFound) - } - - pub async fn get_schedule( - &self, - conn: &mut PoolConnection, - ) -> Result { - DbSchedule::get(conn, self.schedule_id) - .await? - .ok_or(DatabaseError::NotFound) - } - - pub async fn get_macro( - &self, - conn: &mut PoolConnection, - ) -> Result { - DbMacro::get(conn, self.macro_id) - .await? - .ok_or(DatabaseError::NotFound) - } -} diff --git a/emgauwa-common/src/db/mod.rs b/emgauwa-common/src/db/mod.rs deleted file mode 100644 index c4f74b6..0000000 --- a/emgauwa-common/src/db/mod.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::str::FromStr; - -use sqlx::migrate::Migrator; -use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; -use sqlx::{ConnectOptions, Pool, Sqlite}; - -mod controllers; -mod junction_relay_schedule; -mod junction_tag; -mod r#macro; -mod macro_action; -mod model_utils; -mod relays; -mod schedules; -mod tag; - -pub use controllers::DbController; -pub use junction_relay_schedule::DbJunctionRelaySchedule; -pub use junction_tag::DbJunctionTag; -pub use macro_action::DbMacroAction; -pub use r#macro::DbMacro; -pub use relays::DbRelay; -pub use schedules::{DbPeriods, DbSchedule}; -pub use tag::DbTag; - -use crate::errors::{DatabaseError, EmgauwaError}; - -static MIGRATOR: Migrator = sqlx::migrate!("../migrations"); // defaults to "./migrations" - -pub async fn run_migrations(pool: &Pool) -> Result<(), EmgauwaError> { - log::info!("Running migrations"); - MIGRATOR.run(pool).await.map_err(DatabaseError::from)?; - Ok(()) -} - -pub async fn init(db: &str) -> Result, EmgauwaError> { - let options = SqliteConnectOptions::from_str(db)? - .create_if_missing(true) - .log_statements(log::LevelFilter::Trace); - - let pool: Pool = SqlitePoolOptions::new() - .acquire_timeout(std::time::Duration::from_secs(1)) - .max_connections(5) - .connect_with(options) - .await?; - - run_migrations(&pool).await?; - - let mut pool_conn = pool.acquire().await?; - - DbSchedule::get_on(&mut pool_conn).await?; - DbSchedule::get_off(&mut pool_conn).await?; - - Ok(pool) -} diff --git a/emgauwa-common/src/db/model_utils.rs b/emgauwa-common/src/db/model_utils.rs deleted file mode 100644 index 4116365..0000000 --- a/emgauwa-common/src/db/model_utils.rs +++ /dev/null @@ -1,137 +0,0 @@ -use chrono::{NaiveTime, Timelike}; -use serde::{Deserialize, Serialize}; -use sqlx::database::HasArguments; -use sqlx::encode::IsNull; -use sqlx::error::BoxDynError; -use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef}; -use sqlx::{Decode, Encode, Sqlite, Type}; - -use crate::db::DbPeriods; - -#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] -pub struct Period { - #[serde(with = "period_format")] - pub start: NaiveTime, - #[serde(with = "period_format")] - pub end: NaiveTime, -} - -mod period_format { - use chrono::NaiveTime; - use serde::{self, Deserialize, Deserializer, Serializer}; - - const FORMAT: &str = "%H:%M"; - - pub fn serialize(time: &NaiveTime, serializer: S) -> Result - where - S: Serializer, - { - let s = format!("{}", time.format(FORMAT)); - serializer.serialize_str(&s) - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - NaiveTime::parse_from_str(&s, FORMAT).map_err(serde::de::Error::custom) - } -} - -impl Period { - pub fn new(start: NaiveTime, end: NaiveTime) -> Self { - Period { start, end } - } - - pub fn new_on() -> Self { - Period { - start: NaiveTime::MIN, - end: NaiveTime::MIN, - } - } - - pub fn is_on(&self, now: &NaiveTime) -> bool { - self.start.eq(&self.end) || (self.start.le(now) && self.end.gt(now)) - } - - pub fn get_next_time(&self, now: &NaiveTime) -> Option { - if self.start.eq(&self.end) { - // this period is always on - return None; - } - - let start_after_now = self.start.gt(now); - let end_after_now = self.end.gt(now); - let start_before_end = self.start.lt(&self.end); - - match (start_after_now, end_after_now, start_before_end) { - (false, false, _) => None, // both before now - (true, false, _) => Some(self.start), // only start after now - (false, true, _) => Some(self.end), // only end after now - (true, true, true) => Some(self.start), // both after now but start first - (true, true, false) => Some(self.end), // both after now but end first - } - } -} - -impl Type for DbPeriods { - fn type_info() -> SqliteTypeInfo { - <&[u8] as Type>::type_info() - } - - fn compatible(ty: &SqliteTypeInfo) -> bool { - <&[u8] as Type>::compatible(ty) - } -} - -impl<'q> Encode<'q, Sqlite> for DbPeriods { - //noinspection DuplicatedCode - fn encode_by_ref(&self, buf: &mut >::ArgumentBuffer) -> IsNull { - <&Vec as Encode>::encode(&Vec::from(self), buf) - } -} - -impl<'r> Decode<'r, Sqlite> for DbPeriods { - fn decode(value: SqliteValueRef<'r>) -> Result { - let blob = <&[u8] as Decode>::decode(value)?; - Ok(DbPeriods::from(Vec::from(blob))) - } -} - -impl From<&DbPeriods> for Vec { - fn from(periods: &DbPeriods) -> Vec { - periods - .0 - .iter() - .flat_map(|period| { - let vec = vec![ - period.start.hour() as u8, - period.start.minute() as u8, - period.end.hour() as u8, - period.end.minute() as u8, - ]; - vec - }) - .collect() - } -} - -impl From> for DbPeriods { - fn from(value: Vec) -> Self { - let mut vec = Vec::new(); - for i in (3..value.len()).step_by(4) { - let start_val_h: u32 = value[i - 3] as u32; - let start_val_m: u32 = value[i - 2] as u32; - let end_val_h: u32 = value[i - 1] as u32; - let end_val_m: u32 = value[i] as u32; - vec.push(Period { - start: NaiveTime::from_hms_opt(start_val_h, start_val_m, 0) - .expect("Failed to parse period start time from database"), - end: NaiveTime::from_hms_opt(end_val_h, end_val_m, 0) - .expect("Failed to parse period end time from database"), - }); - } - DbPeriods(vec) - } -} diff --git a/emgauwa-common/src/db/relays.rs b/emgauwa-common/src/db/relays.rs deleted file mode 100644 index 2ac448a..0000000 --- a/emgauwa-common/src/db/relays.rs +++ /dev/null @@ -1,177 +0,0 @@ -use std::ops::DerefMut; - -use serde_derive::{Deserialize, Serialize}; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::{DbController, DbJunctionRelaySchedule, DbJunctionTag, DbSchedule, DbTag}; -use crate::errors::DatabaseError; -use crate::types::Weekday; -use crate::utils; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DbRelay { - #[serde(skip)] - pub id: i64, - pub name: String, - pub number: i64, - #[serde(skip)] - pub controller_id: i64, -} - - -impl DbRelay { - pub async fn get_all(conn: &mut PoolConnection) -> Result, DatabaseError> { - sqlx::query_as!(DbRelay, "SELECT * FROM relays") - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get( - conn: &mut PoolConnection, - id: i64, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbRelay, "SELECT * FROM relays WHERE id = ?", id) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_by_controller_and_num( - conn: &mut PoolConnection, - controller: &DbController, - number: i64, - ) -> Result, DatabaseError> { - sqlx::query_as!( - DbRelay, - "SELECT * FROM relays WHERE controller_id = ? AND number = ?", - controller.id, - number - ) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_by_controller_and_num_or_create( - conn: &mut PoolConnection, - controller: &DbController, - number: i64, - new_name: &str, - ) -> Result<(DbRelay, bool), DatabaseError> { - match DbRelay::get_by_controller_and_num(conn, controller, number).await? { - Some(relay) => Ok((relay, false)), - None => { - let relay = DbRelay::create(conn, new_name, number, controller).await?; - Ok((relay, true)) - } - } - } - - pub async fn get_by_tag( - conn: &mut PoolConnection, - tag: &DbTag, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbRelay, "SELECT relay.* FROM relays AS relay INNER JOIN junction_tag ON junction_tag.relay_id = relay.id WHERE junction_tag.tag_id = ?", tag.id) - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn create( - conn: &mut PoolConnection, - new_name: &str, - new_number: i64, - new_controller: &DbController, - ) -> Result { - sqlx::query_as!( - DbRelay, - "INSERT INTO relays (name, number, controller_id) VALUES (?, ?, ?) RETURNING *", - new_name, - new_number, - new_controller.id, - ) - .fetch_optional(conn.deref_mut()) - .await? - .ok_or(DatabaseError::InsertGetError) - } - - pub async fn delete(&self, conn: &mut PoolConnection) -> Result<(), DatabaseError> { - sqlx::query!("DELETE FROM relays WHERE id = ?", self.id) - .execute(conn.deref_mut()) - .await - .map(|res| match res.rows_affected() { - 0 => Err(DatabaseError::DeleteError), - _ => Ok(()), - })? - } - - pub async fn update( - &self, - conn: &mut PoolConnection, - new_name: &str, - ) -> Result { - sqlx::query!("UPDATE relays SET name = ? WHERE id = ?", new_name, self.id,) - .execute(conn.deref_mut()) - .await?; - - DbRelay::get(conn, self.id) - .await? - .ok_or(DatabaseError::UpdateGetError) - } - - pub async fn get_controller( - &self, - conn: &mut PoolConnection, - ) -> Result { - DbController::get(conn, self.controller_id) - .await? - .ok_or(DatabaseError::NotFound) - } - - pub async fn get_tags( - &self, - conn: &mut PoolConnection, - ) -> Result, DatabaseError> { - sqlx::query_scalar!("SELECT tag FROM tags INNER JOIN junction_tag ON junction_tag.tag_id = tags.id WHERE junction_tag.relay_id = ?", self.id) - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn set_tags( - &self, - conn: &mut PoolConnection, - new_tags: &[String], - ) -> Result<(), DatabaseError> { - sqlx::query!("DELETE FROM junction_tag WHERE relay_id = ?", self.id) - .execute(conn.deref_mut()) - .await?; - - for new_tag in new_tags { - let tag: DbTag = DbTag::get_by_tag_or_create(conn, new_tag).await?; - DbJunctionTag::link_relay(conn, &tag, self).await?; - } - Ok(()) - } - - pub async fn reload( - &self, - conn: &mut PoolConnection, - ) -> Result { - Self::get(conn, self.id) - .await? - .ok_or(DatabaseError::NotFound) - } - - pub async fn get_active_schedule( - &self, - conn: &mut PoolConnection, - ) -> Result { - let weekday = utils::get_weekday(); - DbJunctionRelaySchedule::get_schedule(conn, self, weekday as Weekday) - .await? - .ok_or(DatabaseError::NotFound) - } -} diff --git a/emgauwa-common/src/db/schedules.rs b/emgauwa-common/src/db/schedules.rs deleted file mode 100644 index 6a792fd..0000000 --- a/emgauwa-common/src/db/schedules.rs +++ /dev/null @@ -1,209 +0,0 @@ -use std::borrow::Borrow; -use std::ops::DerefMut; - -use chrono::NaiveTime; -use serde_derive::{Deserialize, Serialize}; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::model_utils::Period; -use crate::db::{DbJunctionTag, DbTag}; -use crate::errors::DatabaseError; -use crate::types::ScheduleUid; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DbSchedule { - #[serde(skip)] - pub id: i64, - #[serde(rename = "id")] - pub uid: ScheduleUid, - pub name: String, - pub periods: DbPeriods, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] -pub struct DbPeriods(pub Vec); - -impl DbSchedule { - pub async fn get_all( - conn: &mut PoolConnection, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbSchedule, "SELECT * FROM schedules") - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get( - conn: &mut PoolConnection, - id: i64, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbSchedule, "SELECT * FROM schedules WHERE id = ?", id) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_by_uid( - conn: &mut PoolConnection, - filter_uid: &ScheduleUid, - ) -> Result, DatabaseError> { - sqlx::query_as!( - DbSchedule, - "SELECT * FROM schedules WHERE uid = ?", - filter_uid - ) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_by_tag( - conn: &mut PoolConnection, - tag: &DbTag, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbSchedule, "SELECT schedule.* FROM schedules AS schedule INNER JOIN junction_tag ON junction_tag.schedule_id = schedule.id WHERE junction_tag.tag_id = ?", tag.id) - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn delete_by_uid( - conn: &mut PoolConnection, - filter_uid: ScheduleUid, - ) -> Result<(), DatabaseError> { - let filter_uid = match filter_uid { - ScheduleUid::Off => Err(DatabaseError::Protected), - ScheduleUid::On => Err(DatabaseError::Protected), - ScheduleUid::Any(_) => Ok(filter_uid), - }?; - - if sqlx::query_scalar!("SELECT 1 FROM schedules WHERE uid = ?", filter_uid) - .fetch_optional(conn.deref_mut()) - .await? - .is_none() - { - return Err(DatabaseError::NotFound); - } - - sqlx::query!("DELETE FROM schedules WHERE uid = ?", filter_uid) - .execute(conn.deref_mut()) - .await - .map(|res| match res.rows_affected() { - 0 => Err(DatabaseError::DeleteError), - _ => Ok(()), - })? - } - - pub async fn create( - conn: &mut PoolConnection, - new_uid: ScheduleUid, - new_name: &str, - new_periods: &DbPeriods, - ) -> Result { - sqlx::query_as!( - DbSchedule, - "INSERT INTO schedules (uid, name, periods) VALUES (?, ?, ?) RETURNING *", - new_uid, - new_name, - new_periods, - ) - .fetch_optional(conn.deref_mut()) - .await? - .ok_or(DatabaseError::InsertGetError) - } - - pub async fn get_by_uid_or_create( - conn: &mut PoolConnection, - uid: ScheduleUid, - name: &str, - periods: &DbPeriods, - ) -> Result<(DbSchedule, bool), DatabaseError> { - match DbSchedule::get_by_uid(conn, &uid).await? { - Some(schedule) => Ok((schedule, false)), - None => { - let schedule = DbSchedule::create(conn, uid, name, periods).await?; - Ok((schedule, true)) - } - } - } - - pub async fn get_on(conn: &mut PoolConnection) -> Result { - if let Some(schedule) = DbSchedule::get_by_uid(conn, &ScheduleUid::On).await? { - return Ok(schedule); - } - let periods = DbPeriods(vec![Period::new_on()]); - Self::create(conn, ScheduleUid::On, "On", &periods).await - } - - pub async fn get_off(conn: &mut PoolConnection) -> Result { - if let Some(schedule) = DbSchedule::get_by_uid(conn, &ScheduleUid::Off).await? { - return Ok(schedule); - } - let periods = DbPeriods(vec![]); - Self::create(conn, ScheduleUid::Off, "Off", &periods).await - } - - pub async fn update( - &self, - conn: &mut PoolConnection, - new_name: &str, - new_periods: &DbPeriods, - ) -> Result { - // overwrite periods on protected schedules - let new_periods = match self.uid { - ScheduleUid::Off | ScheduleUid::On => self.periods.borrow(), - ScheduleUid::Any(_) => new_periods, - }; - - sqlx::query!( - "UPDATE schedules SET name = ?, periods = ? WHERE id = ?", - new_name, - new_periods, - self.id, - ) - .execute(conn.deref_mut()) - .await?; - - DbSchedule::get(conn, self.id) - .await? - .ok_or(DatabaseError::UpdateGetError) - } - - pub async fn get_tags( - &self, - conn: &mut PoolConnection, - ) -> Result, DatabaseError> { - Ok(sqlx::query_scalar!("SELECT tag FROM tags INNER JOIN junction_tag ON junction_tag.tag_id = tags.id WHERE junction_tag.schedule_id = ?", self.id) - .fetch_all(conn.deref_mut()) - .await?) - } - - pub async fn set_tags( - &self, - conn: &mut PoolConnection, - new_tags: &[String], - ) -> Result<(), DatabaseError> { - sqlx::query!("DELETE FROM junction_tag WHERE schedule_id = ?", self.id) - .execute(conn.deref_mut()) - .await?; - - for new_tag in new_tags { - let tag: DbTag = DbTag::get_by_tag_or_create(conn, new_tag).await?; - DbJunctionTag::link_schedule(conn, &tag, self).await?; - } - Ok(()) - } - - pub fn is_on(&self, now: &NaiveTime) -> bool { - self.periods.0.iter().any(|period| period.is_on(now)) - } - - pub fn get_next_time(&self, now: &NaiveTime) -> Option { - self.periods - .0 - .iter() - .filter_map(|period| period.get_next_time(now)) - .min() - } -} diff --git a/emgauwa-common/src/db/tag.rs b/emgauwa-common/src/db/tag.rs deleted file mode 100644 index 9c15f79..0000000 --- a/emgauwa-common/src/db/tag.rs +++ /dev/null @@ -1,91 +0,0 @@ -use std::ops::DerefMut; - -use serde_derive::Serialize; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::errors::DatabaseError; - -#[derive(Debug, Serialize, Clone)] -pub struct DbTag { - pub id: i64, - pub tag: String, -} - -impl DbTag { - pub async fn create( - conn: &mut PoolConnection, - new_tag: &str, - ) -> Result { - if new_tag.is_empty() { - return Err(DatabaseError::EmptyDataInsert); - } - - sqlx::query_as!( - DbTag, - "INSERT INTO tags (tag) VALUES (?) RETURNING *", - new_tag - ) - .fetch_optional(conn.deref_mut()) - .await? - .ok_or(DatabaseError::InsertGetError) - } - - pub async fn get_all(conn: &mut PoolConnection) -> Result, DatabaseError> { - sqlx::query_as!(DbTag, "SELECT * FROM tags") - .fetch_all(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get( - conn: &mut PoolConnection, - id: i64, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbTag, "SELECT * FROM tags WHERE id = ?", id) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn get_by_tag_or_create( - conn: &mut PoolConnection, - target_tag: &str, - ) -> Result { - match DbTag::get_by_tag(conn, target_tag).await? { - Some(tag) => Ok(tag), - None => DbTag::create(conn, target_tag).await, - } - } - - pub async fn get_by_tag( - conn: &mut PoolConnection, - target_tag: &str, - ) -> Result, DatabaseError> { - sqlx::query_as!(DbTag, "SELECT * FROM tags WHERE tag = ?", target_tag) - .fetch_optional(conn.deref_mut()) - .await - .map_err(DatabaseError::from) - } - - pub async fn delete_by_tag( - conn: &mut PoolConnection, - filter_tag: &str, - ) -> Result<(), DatabaseError> { - if sqlx::query_scalar!("SELECT 1 FROM tags WHERE tag = ?", filter_tag) - .fetch_optional(conn.deref_mut()) - .await? - .is_none() - { - return Err(DatabaseError::NotFound); - } - - sqlx::query!("DELETE FROM tags WHERE tag = ?", filter_tag) - .execute(conn.deref_mut()) - .await - .map(|res| match res.rows_affected() { - 0 => Err(DatabaseError::DeleteError), - _ => Ok(()), - })? - } -} diff --git a/emgauwa-common/src/drivers/gpio.rs b/emgauwa-common/src/drivers/gpio.rs deleted file mode 100644 index 7847d43..0000000 --- a/emgauwa-common/src/drivers/gpio.rs +++ /dev/null @@ -1,35 +0,0 @@ -use rppal::gpio::{Gpio, OutputPin}; - -use crate::drivers::RelayDriver; -use crate::errors::EmgauwaError; - -pub struct GpioDriver { - pub gpio: OutputPin, - pub inverted: bool, -} - -impl GpioDriver { - pub fn new(pin: u8, inverted: bool) -> Result { - let gpio = Gpio::new()?.get(pin)?.into_output(); - Ok(Self { gpio, inverted }) - } -} - -impl RelayDriver for GpioDriver { - fn set(&mut self, value: bool) -> Result<(), EmgauwaError> { - if self.get_high(value) { - self.gpio.set_high(); - } else { - self.gpio.set_low(); - } - Ok(()) - } - - fn get_pin(&self) -> u8 { - self.gpio.pin() - } - - fn get_inverted(&self) -> bool { - self.inverted - } -} diff --git a/emgauwa-common/src/drivers/mod.rs b/emgauwa-common/src/drivers/mod.rs deleted file mode 100644 index abae75a..0000000 --- a/emgauwa-common/src/drivers/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod gpio; -mod null; -mod piface; - -pub use gpio::GpioDriver; -pub use null::NullDriver; -pub use piface::PiFaceDriver; - -use crate::errors::EmgauwaError; - -pub trait RelayDriver { - fn get_high(&self, value: bool) -> bool { - value ^ self.get_inverted() - } - - fn set(&mut self, value: bool) -> Result<(), EmgauwaError>; - fn get_pin(&self) -> u8; - fn get_inverted(&self) -> bool; -} diff --git a/emgauwa-common/src/drivers/null.rs b/emgauwa-common/src/drivers/null.rs deleted file mode 100644 index be4e8b9..0000000 --- a/emgauwa-common/src/drivers/null.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::drivers::RelayDriver; -use crate::errors::EmgauwaError; - -pub struct NullDriver { - pub pin: u8, -} - -impl NullDriver { - pub fn new(pin: u8) -> Self { - Self { pin } - } -} - -impl RelayDriver for NullDriver { - fn set(&mut self, _value: bool) -> Result<(), EmgauwaError> { - Ok(()) - } - - fn get_pin(&self) -> u8 { - self.pin - } - - fn get_inverted(&self) -> bool { - false - } -} diff --git a/emgauwa-common/src/drivers/piface.rs b/emgauwa-common/src/drivers/piface.rs deleted file mode 100644 index 7bc20da..0000000 --- a/emgauwa-common/src/drivers/piface.rs +++ /dev/null @@ -1,52 +0,0 @@ -use rppal_pfd::{ - ChipSelect, HardwareAddress, OutputPin, PiFaceDigital, PiFaceDigitalError, SpiBus, SpiMode, -}; - -use crate::drivers::RelayDriver; -use crate::errors::EmgauwaError; - -pub struct PiFaceDriver { - pub pfd_pin: OutputPin, -} - -impl PiFaceDriver { - pub fn new(pin: u8, pfd: &Option) -> Result { - let pfd = pfd.as_ref().ok_or(EmgauwaError::Hardware(String::from( - "PiFaceDigital not initialized", - )))?; - let pfd_pin = pfd.get_output_pin(pin)?; - Ok(Self { pfd_pin }) - } - - pub fn init_piface() -> Result { - let mut pfd = PiFaceDigital::new( - HardwareAddress::new(0)?, - SpiBus::Spi0, - ChipSelect::Cs0, - 100_000, - SpiMode::Mode0, - )?; - pfd.init()?; - - Ok(pfd) - } -} - -impl RelayDriver for PiFaceDriver { - fn set(&mut self, value: bool) -> Result<(), EmgauwaError> { - if self.get_high(value) { - self.pfd_pin.set_high().map_err(PiFaceDigitalError::from)?; - } else { - self.pfd_pin.set_low().map_err(PiFaceDigitalError::from)?; - } - Ok(()) - } - - fn get_pin(&self) -> u8 { - self.pfd_pin.get_pin_number() - } - - fn get_inverted(&self) -> bool { - false - } -} diff --git a/emgauwa-common/src/errors/api_error.rs b/emgauwa-common/src/errors/api_error.rs deleted file mode 100644 index bba2248..0000000 --- a/emgauwa-common/src/errors/api_error.rs +++ /dev/null @@ -1,22 +0,0 @@ -use actix_web::http::StatusCode; - -#[derive(Debug)] -pub enum ApiError { - ProtectedSchedule, -} - -impl ApiError { - pub fn get_code(&self) -> StatusCode { - match self { - ApiError::ProtectedSchedule => StatusCode::FORBIDDEN, - } - } -} - -impl From<&ApiError> for String { - fn from(err: &ApiError) -> Self { - match err { - ApiError::ProtectedSchedule => String::from("the targeted schedule is protected"), - } - } -} diff --git a/emgauwa-common/src/errors/database_error.rs b/emgauwa-common/src/errors/database_error.rs deleted file mode 100644 index cd9f838..0000000 --- a/emgauwa-common/src/errors/database_error.rs +++ /dev/null @@ -1,85 +0,0 @@ -use actix_web::http::StatusCode; -use actix_web::HttpResponse; -use serde::ser::SerializeStruct; -use serde::{Serialize, Serializer}; -use sqlx::migrate::MigrateError; -use sqlx::Error; - -#[derive(Debug)] -pub enum DatabaseError { - DeleteError, - InsertError, - InsertGetError, - NotFound, - Protected, - EmptyDataInsert, - UpdateError, - UpdateGetError, - MigrationError(MigrateError), - Unknown(Error), -} - -impl DatabaseError { - pub fn get_code(&self) -> StatusCode { - match self { - DatabaseError::NotFound => StatusCode::NOT_FOUND, - DatabaseError::Protected => StatusCode::FORBIDDEN, - _ => StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl Serialize for DatabaseError { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut s = serializer.serialize_struct("error", 3)?; - s.serialize_field("type", "database-error")?; - s.serialize_field("code", &self.get_code().as_u16())?; - s.serialize_field("description", &String::from(self))?; - s.end() - } -} - -impl From<&DatabaseError> for String { - fn from(err: &DatabaseError) -> Self { - String::from(match err { - DatabaseError::InsertError => "error on inserting into database", - DatabaseError::InsertGetError => { - "error on retrieving new entry from database (your entry was saved)" - } - DatabaseError::NotFound => "model was not found in database", - DatabaseError::DeleteError => "error on deleting from database", - DatabaseError::Protected => "model is protected", - DatabaseError::UpdateError => "error on updating the model", - DatabaseError::UpdateGetError => { - "error on retrieving updated model from database (your entry was saved)" - } - DatabaseError::MigrationError(_) => "error on running migrations", - DatabaseError::Unknown(_) => "unknown error", - DatabaseError::EmptyDataInsert => "tried to insert empty data", - }) - } -} - -impl From for HttpResponse { - fn from(err: DatabaseError) -> Self { - HttpResponse::build(err.get_code()).json(err) - } -} - -impl From for DatabaseError { - fn from(value: Error) -> Self { - match value { - Error::RowNotFound => DatabaseError::NotFound, - _ => DatabaseError::Unknown(value), - } - } -} - -impl From for DatabaseError { - fn from(value: MigrateError) -> Self { - Self::MigrationError(value) - } -} diff --git a/emgauwa-common/src/errors/emgauwa_error.rs b/emgauwa-common/src/errors/emgauwa_error.rs deleted file mode 100644 index 0e8ce65..0000000 --- a/emgauwa-common/src/errors/emgauwa_error.rs +++ /dev/null @@ -1,161 +0,0 @@ -use std::error::Error; -use std::fmt::{Debug, Display, Formatter}; -use std::io::ErrorKind; - -use actix::MailboxError; -use actix_web::http::StatusCode; -use actix_web::HttpResponse; -use config::ConfigError; -use rppal::gpio; -use rppal_mcp23s17::Mcp23s17Error; -use rppal_pfd::PiFaceDigitalError; -use serde::ser::SerializeStruct; -use serde::{Serialize, Serializer}; - -use crate::errors::{ApiError, DatabaseError}; -use crate::types::EmgauwaUid; - -#[derive(Debug)] -pub enum EmgauwaError { - Api(ApiError), - Uid(uuid::Error), - Serialization(serde_json::Error), - Database(DatabaseError), - Other(String), - Internal(String), - Connection(EmgauwaUid), - Hardware(String), -} - -impl EmgauwaError { - fn get_code(&self) -> StatusCode { - match self { - EmgauwaError::Api(err) => err.get_code(), - EmgauwaError::Serialization(_) => StatusCode::INTERNAL_SERVER_ERROR, - EmgauwaError::Database(err) => err.get_code(), - EmgauwaError::Uid(_) => StatusCode::BAD_REQUEST, - EmgauwaError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, - EmgauwaError::Connection(_) => StatusCode::GATEWAY_TIMEOUT, - EmgauwaError::Other(_) => StatusCode::INTERNAL_SERVER_ERROR, - EmgauwaError::Hardware(_) => StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl From<&EmgauwaError> for String { - fn from(err: &EmgauwaError) -> Self { - match err { - EmgauwaError::Api(err) => String::from(err), - EmgauwaError::Serialization(_) => String::from("error during (de-)serialization"), - EmgauwaError::Database(err) => String::from(err), - EmgauwaError::Uid(_) => String::from("the uid is in a bad format"), - EmgauwaError::Internal(_) => String::from("internal error"), - EmgauwaError::Connection(_) => String::from("the target controller is not connected"), - EmgauwaError::Other(err) => format!("other error: {}", err), - EmgauwaError::Hardware(err) => format!("hardware error: {}", err), - } - } -} - -impl From for EmgauwaError { - fn from(value: ApiError) -> Self { - EmgauwaError::Api(value) - } -} - -impl From for EmgauwaError { - fn from(value: DatabaseError) -> Self { - EmgauwaError::Database(value) - } -} - -impl From for EmgauwaError { - fn from(value: serde_json::Error) -> Self { - EmgauwaError::Serialization(value) - } -} - -impl From for EmgauwaError { - fn from(value: sqlx::Error) -> Self { - EmgauwaError::Database(DatabaseError::from(value)) - } -} - -impl From for EmgauwaError { - fn from(value: uuid::Error) -> Self { - EmgauwaError::Uid(value) - } -} - -impl From for EmgauwaError { - fn from(value: MailboxError) -> Self { - EmgauwaError::Internal(value.to_string()) - } -} - -impl From for EmgauwaError { - fn from(value: ConfigError) -> Self { - Self::Other(value.to_string()) - } -} - -impl From for EmgauwaError { - fn from(value: gpio::Error) -> Self { - Self::Hardware(value.to_string()) - } -} - -impl From for EmgauwaError { - fn from(value: PiFaceDigitalError) -> Self { - match value { - PiFaceDigitalError::Mcp23s17Error { source } => match source { - Mcp23s17Error::SpiError { source } => Self::Hardware(source.to_string()), - _ => Self::Hardware(source.to_string()), - }, - PiFaceDigitalError::GpioError { source } => Self::Hardware(source.to_string()), - _ => Self::Hardware(value.to_string()), - } - } -} - -impl From<&EmgauwaError> for HttpResponse { - fn from(err: &EmgauwaError) -> Self { - HttpResponse::build(err.get_code()).json(err) - } -} - -impl Error for EmgauwaError {} - -impl From for std::io::Error { - fn from(value: EmgauwaError) -> Self { - std::io::Error::new(ErrorKind::Other, value) - } -} - -impl Serialize for EmgauwaError { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut s = serializer.serialize_struct("error", 2)?; - s.serialize_field("code", &self.get_code().as_u16())?; - s.serialize_field("description", &String::from(self))?; - s.end() - } -} - -impl Display for EmgauwaError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}: {}", self.get_code(), String::from(self)) - } -} - -impl actix_web::error::ResponseError for EmgauwaError { - fn status_code(&self) -> StatusCode { - self.get_code() - } - - fn error_response(&self) -> HttpResponse { - HttpResponse::from(self) - } -} diff --git a/emgauwa-common/src/errors/mod.rs b/emgauwa-common/src/errors/mod.rs deleted file mode 100644 index 0209bcc..0000000 --- a/emgauwa-common/src/errors/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod api_error; -mod database_error; -mod emgauwa_error; - -pub use api_error::ApiError; -pub use database_error::DatabaseError; -pub use emgauwa_error::EmgauwaError; diff --git a/emgauwa-common/src/lib.rs b/emgauwa-common/src/lib.rs deleted file mode 100644 index 14609b9..0000000 --- a/emgauwa-common/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod constants; -pub mod db; -pub mod drivers; -pub mod errors; -pub mod models; -pub mod settings; -pub mod types; -pub mod utils; diff --git a/emgauwa-common/src/models/controller.rs b/emgauwa-common/src/models/controller.rs deleted file mode 100644 index 96d7b00..0000000 --- a/emgauwa-common/src/models/controller.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::time::Instant; - -use actix::MessageResponse; -use chrono::NaiveTime; -use futures::executor::block_on; -use serde_derive::{Deserialize, Serialize}; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::DbController; -use crate::errors::{DatabaseError, EmgauwaError}; -use crate::models::{convert_db_list_cache, FromDbModel, Relay}; -use crate::types::RelayStates; - -#[derive(Serialize, Deserialize, Debug, Clone, MessageResponse)] -pub struct Controller { - #[serde(flatten)] - pub c: DbController, - pub relays: Vec, -} - -impl FromDbModel for Controller { - type DbModel = DbController; - type DbModelCache = Vec; - - fn from_db_model( - conn: &mut PoolConnection, - db_model: Self::DbModel, - ) -> Result { - let relays_db = block_on(db_model.get_relays(conn))?; - let cache = convert_db_list_cache(conn, relays_db, db_model.clone())?; - Self::from_db_model_cache(conn, db_model, cache) - } - - fn from_db_model_cache( - _conn: &mut PoolConnection, - db_model: Self::DbModel, - cache: Self::DbModelCache, - ) -> Result { - Ok(Controller { - c: db_model, - relays: cache, - }) - } -} - -impl Controller { - pub fn reload(&mut self, conn: &mut PoolConnection) -> Result<(), EmgauwaError> { - self.c = block_on(self.c.reload(conn))?; - for relay in &mut self.relays { - relay.reload(conn)?; - } - Ok(()) - } - - pub fn apply_relay_states(&mut self, relay_states: &RelayStates) { - self.relays - .iter_mut() - .zip(relay_states.iter()) - .for_each(|(relay, is_on)| { - relay.is_on = *is_on; - }); - } - - pub fn get_relay_states(&self) -> RelayStates { - self.relays.iter().map(|r| r.is_on).collect() - } - - pub fn get_next_time(&self, now: &NaiveTime) -> Option { - self.relays - .iter() - .filter_map(|r| r.active_schedule.get_next_time(now)) - .min() - } - - pub fn relay_pulse(&mut self, relay_num: i64, until: Instant) -> Result<(), EmgauwaError> { - let relay = self - .relays - .iter_mut() - .find(|r| r.r.number == relay_num) - .ok_or(EmgauwaError::Other(String::from("Relay not found")))?; - - relay.pulsing = Some(until); - Ok(()) - } -} diff --git a/emgauwa-common/src/models/macro.rs b/emgauwa-common/src/models/macro.rs deleted file mode 100644 index fbe9dbb..0000000 --- a/emgauwa-common/src/models/macro.rs +++ /dev/null @@ -1,42 +0,0 @@ -use futures::executor::block_on; -use serde_derive::{Deserialize, Serialize}; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::DbMacro; -use crate::errors::DatabaseError; -use crate::models::{convert_db_list, FromDbModel, MacroAction}; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Macro { - #[serde(flatten)] - pub m: DbMacro, - pub actions: Vec, -} - - -impl FromDbModel for Macro { - type DbModel = DbMacro; - type DbModelCache = (); - - fn from_db_model( - conn: &mut PoolConnection, - db_model: Self::DbModel, - ) -> Result { - Self::from_db_model_cache(conn, db_model, ()) - } - - fn from_db_model_cache( - conn: &mut PoolConnection, - db_model: Self::DbModel, - _cache: Self::DbModelCache, - ) -> Result { - let actions_db = block_on(db_model.get_actions(conn))?; - let actions: Vec = convert_db_list(conn, actions_db)?; - - Ok(Macro { - m: db_model, - actions, - }) - } -} diff --git a/emgauwa-common/src/models/macro_action.rs b/emgauwa-common/src/models/macro_action.rs deleted file mode 100644 index d7b5aca..0000000 --- a/emgauwa-common/src/models/macro_action.rs +++ /dev/null @@ -1,56 +0,0 @@ -use futures::executor::block_on; -use serde_derive::{Deserialize, Serialize}; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::{DbJunctionRelaySchedule, DbMacroAction}; -use crate::errors::{DatabaseError, EmgauwaError}; -use crate::models::{FromDbModel, Relay, Schedule}; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct MacroAction { - pub schedule: Schedule, - pub relay: Relay, - pub weekday: i64, -} - - -impl FromDbModel for MacroAction { - type DbModel = DbMacroAction; - type DbModelCache = (); - - fn from_db_model( - conn: &mut PoolConnection, - db_model: Self::DbModel, - ) -> Result { - Self::from_db_model_cache(conn, db_model, ()) - } - - fn from_db_model_cache( - conn: &mut PoolConnection, - db_model: Self::DbModel, - _cache: Self::DbModelCache, - ) -> Result { - let schedule_db = block_on(db_model.get_schedule(conn))?; - let schedule = Schedule::from_db_model(conn, schedule_db)?; - - let relay_db = block_on(db_model.get_relay(conn))?; - let relay = Relay::from_db_model(conn, relay_db)?; - - let weekday = db_model.weekday; - - Ok(MacroAction { - schedule, - relay, - weekday, - }) - } -} - -impl MacroAction { - pub async fn execute(&self, conn: &mut PoolConnection) -> Result<(), EmgauwaError> { - DbJunctionRelaySchedule::set_schedule(conn, &self.relay.r, &self.schedule.s, self.weekday) - .await?; - Ok(()) - } -} diff --git a/emgauwa-common/src/models/mod.rs b/emgauwa-common/src/models/mod.rs deleted file mode 100644 index 570bf48..0000000 --- a/emgauwa-common/src/models/mod.rs +++ /dev/null @@ -1,68 +0,0 @@ -mod controller; -mod r#macro; -mod macro_action; -mod relay; -mod schedule; -mod tag; - -pub use controller::Controller; -pub use macro_action::MacroAction; -pub use r#macro::Macro; -pub use relay::Relay; -pub use schedule::Schedule; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; -pub use tag::Tag; - -use crate::errors::DatabaseError; - -pub trait FromDbModel { - type DbModel: Clone; - type DbModelCache: Clone; - - fn from_db_model( - conn: &mut PoolConnection, - db_model: Self::DbModel, - ) -> Result - where - Self: Sized; - - fn from_db_model_cache( - conn: &mut PoolConnection, - db_model: Self::DbModel, - cache: Self::DbModelCache, - ) -> Result - where - Self: Sized; -} - -fn convert_db_list_generic( - conn: &mut PoolConnection, - db_models: Vec, - cache: Option, -) -> Result, DatabaseError> { - let mut result: Vec = Vec::new(); - for db_model in db_models { - let new = match &cache { - Some(c) => T::from_db_model_cache(conn, db_model, c.clone()), - None => T::from_db_model(conn, db_model), - }?; - result.push(new); - } - Ok(result) -} - -pub fn convert_db_list( - conn: &mut PoolConnection, - db_models: Vec, -) -> Result, DatabaseError> { - convert_db_list_generic(conn, db_models, None) -} - -pub fn convert_db_list_cache( - conn: &mut PoolConnection, - db_models: Vec, - cache: T::DbModelCache, -) -> Result, DatabaseError> { - convert_db_list_generic(conn, db_models, Some(cache)) -} diff --git a/emgauwa-common/src/models/relay.rs b/emgauwa-common/src/models/relay.rs deleted file mode 100644 index a7d0e60..0000000 --- a/emgauwa-common/src/models/relay.rs +++ /dev/null @@ -1,107 +0,0 @@ -use std::time::Instant; - -use chrono::NaiveTime; -use futures::executor::block_on; -use serde_derive::{Deserialize, Serialize}; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule}; -use crate::errors::DatabaseError; -use crate::models::FromDbModel; -use crate::types::EmgauwaUid; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Relay { - #[serde(flatten)] - pub r: DbRelay, - pub controller: DbController, - pub controller_id: EmgauwaUid, - pub schedules: Vec, - pub active_schedule: DbSchedule, - pub is_on: Option, - pub tags: Vec, - - // for internal use only. - #[serde(skip)] - pub pulsing: Option, -} - - -impl FromDbModel for Relay { - type DbModel = DbRelay; - type DbModelCache = DbController; - - fn from_db_model( - conn: &mut PoolConnection, - db_model: Self::DbModel, - ) -> Result { - let cache = block_on(db_model.get_controller(conn))?; - Self::from_db_model_cache(conn, db_model, cache) - } - - fn from_db_model_cache( - conn: &mut PoolConnection, - db_model: Self::DbModel, - cache: Self::DbModelCache, - ) -> Result { - let tags = block_on(db_model.get_tags(conn))?; - let controller_id = cache.uid.clone(); - - let schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &db_model))?; - let active_schedule = block_on(db_model.get_active_schedule(conn))?; - - let is_on = None; - - Ok(Relay { - r: db_model, - controller: cache, - controller_id, - schedules, - active_schedule, - is_on, - tags, - pulsing: None, - }) - } -} - -impl Relay { - pub fn reload(&mut self, conn: &mut PoolConnection) -> Result<(), DatabaseError> { - self.r = block_on(self.r.reload(conn))?; - self.schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &self.r))?; - self.reload_active_schedule(conn)?; - - Ok(()) - } - - pub fn reload_active_schedule( - &mut self, - conn: &mut PoolConnection, - ) -> Result<(), DatabaseError> { - self.active_schedule = block_on(self.r.get_active_schedule(conn))?; - Ok(()) - } - - pub fn is_on(&self, now: &NaiveTime) -> bool { - self.active_schedule.is_on(now) - } - - pub fn get_next_time(&self, now: &NaiveTime) -> Option { - self.active_schedule.get_next_time(now) - } - - pub fn check_pulsing(&mut self, now: &Instant) -> Option { - match self.pulsing { - Some(dur_instant) => { - if dur_instant.lt(now) { - self.pulsing = None; - None - } else { - Some(dur_instant) - } - } - None => None, - } - } -} diff --git a/emgauwa-common/src/models/schedule.rs b/emgauwa-common/src/models/schedule.rs deleted file mode 100644 index 9bf07b4..0000000 --- a/emgauwa-common/src/models/schedule.rs +++ /dev/null @@ -1,41 +0,0 @@ -use futures::executor::block_on; -use serde_derive::{Deserialize, Serialize}; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::DbSchedule; -use crate::errors::DatabaseError; -use crate::models::FromDbModel; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Schedule { - #[serde(flatten)] - pub s: DbSchedule, - pub tags: Vec, -} - -impl FromDbModel for Schedule { - type DbModel = DbSchedule; - type DbModelCache = Vec; - - fn from_db_model( - conn: &mut PoolConnection, - db_model: Self::DbModel, - ) -> Result { - let cache = block_on(db_model.get_tags(conn))?; - Self::from_db_model_cache(conn, db_model, cache) - } - - fn from_db_model_cache( - _conn: &mut PoolConnection, - db_model: Self::DbModel, - cache: Self::DbModelCache, - ) -> Result { - let schedule = db_model.clone(); - - Ok(Schedule { - s: schedule, - tags: cache, - }) - } -} diff --git a/emgauwa-common/src/models/tag.rs b/emgauwa-common/src/models/tag.rs deleted file mode 100644 index 7e23fb4..0000000 --- a/emgauwa-common/src/models/tag.rs +++ /dev/null @@ -1,49 +0,0 @@ -use actix::MessageResponse; -use futures::executor::block_on; -use serde_derive::{Deserialize, Serialize}; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::{DbRelay, DbSchedule, DbTag}; -use crate::errors::DatabaseError; -use crate::models::{convert_db_list, FromDbModel, Relay, Schedule}; - -#[derive(Serialize, Deserialize, Debug, Clone, MessageResponse)] -pub struct Tag { - pub tag: String, - pub relays: Vec, - pub schedules: Vec, -} - -impl FromDbModel for Tag { - type DbModel = DbTag; - type DbModelCache = (Vec, Vec); - - fn from_db_model( - conn: &mut PoolConnection, - db_model: Self::DbModel, - ) -> Result { - let db_schedules = block_on(DbSchedule::get_by_tag(conn, &db_model))?; - let schedules: Vec = convert_db_list(conn, db_schedules)?; - - let db_relays = block_on(DbRelay::get_by_tag(conn, &db_model))?; - let relays: Vec = convert_db_list(conn, db_relays)?; - - let cache = (relays, schedules); - Self::from_db_model_cache(conn, db_model, cache) - } - - fn from_db_model_cache( - _conn: &mut PoolConnection, - db_model: Self::DbModel, - cache: Self::DbModelCache, - ) -> Result { - let tag = db_model.tag.clone(); - let (relays, schedules) = cache; - Ok(Tag { - tag, - relays, - schedules, - }) - } -} diff --git a/emgauwa-common/src/settings.rs b/emgauwa-common/src/settings.rs deleted file mode 100644 index bc966dd..0000000 --- a/emgauwa-common/src/settings.rs +++ /dev/null @@ -1,67 +0,0 @@ -use serde_derive::Deserialize; - -use crate::constants; -use crate::errors::EmgauwaError; - -#[derive(Clone, Debug, Deserialize)] -#[serde(default)] -#[allow(unused)] -pub struct Server { - pub host: String, - pub port: u16, -} - -#[derive(Clone, Debug, Deserialize)] -#[serde(default)] -#[allow(unused)] -pub struct Logging { - pub level: String, - pub file: String, -} - -#[derive(Clone, Debug, Deserialize, Default)] -#[serde(default)] -#[allow(unused)] -pub struct Permissions { - pub user: String, - pub group: String, -} - -impl Default for Server { - fn default() -> Self { - Server { - host: String::from("127.0.0.1"), - port: constants::DEFAULT_PORT, - } - } -} - -impl Default for Logging { - fn default() -> Self { - Logging { - level: String::from("info"), - file: String::from("stdout"), - } - } -} - -pub fn load(config_name: &str, env_prefix: &str) -> Result -where - for<'de> T: serde::Deserialize<'de>, -{ - let etc_file = - config::File::with_name(&format!("/etc/emgauwa/{}", config_name)).required(false); - let local_file = config::File::with_name(&format!("./emgauwa-{}", config_name)).required(false); - - config::Config::builder() - .add_source(etc_file) - .add_source(local_file) - .add_source( - config::Environment::with_prefix(&format!("EMGAUWA_{}", env_prefix)) - .prefix_separator("__") - .separator("__"), - ) - .build()? - .try_deserialize::() - .map_err(EmgauwaError::from) -} diff --git a/emgauwa-common/src/types/emgauwa_uid.rs b/emgauwa-common/src/types/emgauwa_uid.rs deleted file mode 100644 index 8d29fbd..0000000 --- a/emgauwa-common/src/types/emgauwa_uid.rs +++ /dev/null @@ -1,103 +0,0 @@ -use std::fmt::{Display, Formatter}; -use std::str::FromStr; - -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use sqlx::database::HasArguments; -use sqlx::encode::IsNull; -use sqlx::error::BoxDynError; -use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef}; -use sqlx::{Decode, Encode, Sqlite, Type}; -use uuid::Uuid; - -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub struct EmgauwaUid(Uuid); - -impl Default for EmgauwaUid { - fn default() -> Self { - Self(Uuid::new_v4()) - } -} - -impl Display for EmgauwaUid { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", String::from(self)) - } -} - -impl Serialize for EmgauwaUid { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - String::from(self).serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for EmgauwaUid { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Self::try_from(String::deserialize(deserializer)?.as_str()) - .map_err(|_| serde::de::Error::custom("invalid uid")) - } -} - -impl From<&EmgauwaUid> for String { - fn from(uid: &EmgauwaUid) -> String { - uid.0.as_hyphenated().to_string() - } -} - -impl Type for EmgauwaUid { - fn type_info() -> SqliteTypeInfo { - <&[u8] as Type>::type_info() - } - - fn compatible(ty: &SqliteTypeInfo) -> bool { - <&[u8] as Type>::compatible(ty) - } -} - -impl<'q> Encode<'q, Sqlite> for EmgauwaUid { - //noinspection DuplicatedCode - fn encode_by_ref(&self, buf: &mut >::ArgumentBuffer) -> IsNull { - as Encode>::encode(Vec::from(self), buf) - } -} - -impl<'r> Decode<'r, Sqlite> for EmgauwaUid { - //noinspection DuplicatedCode - fn decode(value: SqliteValueRef<'r>) -> Result { - Self::try_from(<&[u8] as Decode>::decode(value)?).map_err(Into::into) - } -} - -impl From<&EmgauwaUid> for Vec { - fn from(uid: &EmgauwaUid) -> Vec { - uid.0.as_bytes().to_vec() - } -} - -impl TryFrom<&str> for EmgauwaUid { - type Error = uuid::Error; - - fn try_from(value: &str) -> Result { - let uuid = Uuid::from_str(value)?; - Ok(Self(uuid)) - } -} - -impl TryFrom<&[u8]> for EmgauwaUid { - type Error = uuid::Error; - - fn try_from(value: &[u8]) -> Result { - Ok(Self(Uuid::from_slice(value)?)) - } -} - -impl From> for EmgauwaUid { - fn from(value: Vec) -> Self { - Self::try_from(value.as_slice()).expect("Failed to parse uid from database") - } -} diff --git a/emgauwa-common/src/types/mod.rs b/emgauwa-common/src/types/mod.rs deleted file mode 100644 index dbbfb77..0000000 --- a/emgauwa-common/src/types/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -mod emgauwa_uid; -mod request; -mod schedule_uid; - -use actix::Message; -pub use emgauwa_uid::EmgauwaUid; -pub use request::*; -pub use schedule_uid::ScheduleUid; -use serde_derive::{Deserialize, Serialize}; - -use crate::db::DbSchedule; -use crate::errors::EmgauwaError; -use crate::models::{Controller, Relay}; - -pub type Weekday = i64; - -pub type RelayStates = Vec>; - -#[derive(Debug, Serialize, Deserialize, Message)] -#[rtype(result = "Result<(), EmgauwaError>")] -pub enum ControllerWsAction { - Register(Controller), - Disconnect, - Schedules(Vec), - Relays(Vec), - Controller(Controller), - RelayStates((EmgauwaUid, RelayStates)), - RelayPulse((i64, Option)), -} diff --git a/emgauwa-common/src/types/request.rs b/emgauwa-common/src/types/request.rs deleted file mode 100644 index 5c9a927..0000000 --- a/emgauwa-common/src/types/request.rs +++ /dev/null @@ -1,95 +0,0 @@ -use serde_derive::{Deserialize, Serialize}; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::db::{DbPeriods, DbSchedule}; -use crate::errors::DatabaseError; -use crate::types::{EmgauwaUid, ScheduleUid}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestScheduleCreate { - pub name: String, - pub periods: DbPeriods, - pub tags: Option>, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestScheduleUpdate { - pub name: Option, - pub periods: Option, - pub tags: Option>, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestRelayUpdate { - pub name: Option, - pub active_schedule: Option, - pub schedules: Option>, - pub tags: Option>, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestRelayPulse { - pub duration: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestScheduleId { - pub id: ScheduleUid, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestControllerUpdate { - pub name: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestTagCreate { - pub tag: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestMacroActionRelay { - pub number: i64, - pub controller_id: EmgauwaUid, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestMacroActionSchedule { - pub id: ScheduleUid, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestMacroAction { - pub weekday: i64, - pub relay: RequestMacroActionRelay, - pub schedule: RequestMacroActionSchedule, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestMacroCreate { - pub name: String, - pub actions: Vec, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestMacroUpdate { - pub name: Option, - pub actions: Option>, -} - -#[derive(Debug, Deserialize)] -pub struct RequestMacroExecute { - pub weekday: Option, -} - -impl RequestScheduleId { - pub async fn get_schedule( - &self, - conn: &mut PoolConnection, - ) -> Result { - DbSchedule::get_by_uid(conn, &self.id) - .await? - .ok_or(DatabaseError::NotFound) - } -} diff --git a/emgauwa-common/src/types/schedule_uid.rs b/emgauwa-common/src/types/schedule_uid.rs deleted file mode 100644 index b538df9..0000000 --- a/emgauwa-common/src/types/schedule_uid.rs +++ /dev/null @@ -1,171 +0,0 @@ -use std::convert::TryFrom; -use std::fmt::{Debug, Formatter}; -use std::str::FromStr; - -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use sqlx::database::HasArguments; -use sqlx::encode::IsNull; -use sqlx::error::BoxDynError; -use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef}; -use sqlx::{Decode, Encode, Sqlite, Type}; -use uuid::Uuid; - -#[derive(Clone)] -pub enum ScheduleUid { - Off, - On, - Any(Uuid), -} - -impl ScheduleUid { - const OFF_STR: &'static str = "off"; - const OFF_U128: u128 = 0; - const OFF_U8: u8 = 0; - const ON_STR: &'static str = "on"; - const ON_U128: u128 = 1; - const ON_U8: u8 = 1; -} - -impl Default for ScheduleUid { - fn default() -> Self { - Self::Any(Uuid::new_v4()) - } -} - -impl Debug for ScheduleUid { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Self::Off => Self::OFF_STR.fmt(f), - Self::On => Self::ON_STR.fmt(f), - Self::Any(value) => value.fmt(f), - } - } -} - -impl PartialEq for ScheduleUid { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::Off, Self::Off) => true, - (Self::On, Self::On) => true, - (Self::Any(my_uuid), Self::Any(other_uuid)) => my_uuid == other_uuid, - _ => false, - } - } -} - -impl Type for ScheduleUid { - fn type_info() -> SqliteTypeInfo { - <&[u8] as Type>::type_info() - } - - fn compatible(ty: &SqliteTypeInfo) -> bool { - <&[u8] as Type>::compatible(ty) - } -} - -impl<'q> Encode<'q, Sqlite> for ScheduleUid { - //noinspection DuplicatedCode - fn encode_by_ref(&self, buf: &mut >::ArgumentBuffer) -> IsNull { - as Encode>::encode(Vec::from(self), buf) - } -} - -impl<'r> Decode<'r, Sqlite> for ScheduleUid { - //noinspection DuplicatedCode - fn decode(value: SqliteValueRef<'r>) -> Result { - Self::try_from(<&[u8] as Decode>::decode(value)?).map_err(Into::into) - } -} - -impl Serialize for ScheduleUid { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - String::from(self).serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for ScheduleUid { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Self::try_from(String::deserialize(deserializer)?.as_str()) - .map_err(|_| serde::de::Error::custom("invalid schedule uid")) - } -} - -impl From for ScheduleUid { - fn from(uid: Uuid) -> Self { - match uid.as_u128() { - Self::OFF_U128 => Self::Off, - Self::ON_U128 => Self::On, - _ => Self::Any(uid), - } - } -} - -impl TryFrom<&str> for ScheduleUid { - type Error = uuid::Error; - - fn try_from(value: &str) -> Result { - match value { - Self::OFF_STR => Ok(Self::Off), - Self::ON_STR => Ok(Self::On), - any => match Uuid::from_str(any) { - Ok(uuid) => Ok(Self::Any(uuid)), - Err(err) => Err(err), - }, - } - } -} - -impl From<&ScheduleUid> for Uuid { - fn from(uid: &ScheduleUid) -> Uuid { - match uid { - ScheduleUid::Off => Uuid::from_u128(ScheduleUid::OFF_U128), - ScheduleUid::On => Uuid::from_u128(ScheduleUid::ON_U128), - ScheduleUid::Any(value) => *value, - } - } -} - -impl From<&ScheduleUid> for String { - fn from(uid: &ScheduleUid) -> String { - match uid { - ScheduleUid::Off => String::from(ScheduleUid::OFF_STR), - ScheduleUid::On => String::from(ScheduleUid::ON_STR), - ScheduleUid::Any(value) => value.as_hyphenated().to_string(), - } - } -} - -impl From<&ScheduleUid> for Vec { - fn from(uid: &ScheduleUid) -> Vec { - match uid { - ScheduleUid::Off => vec![ScheduleUid::OFF_U8], - ScheduleUid::On => vec![ScheduleUid::ON_U8], - ScheduleUid::Any(value) => value.as_bytes().to_vec(), - } - } -} - -impl TryFrom<&[u8]> for ScheduleUid { - type Error = uuid::Error; - - fn try_from(value: &[u8]) -> Result { - let result = match value { - [Self::OFF_U8] => Self::Off, - [Self::ON_U8] => Self::On, - value_bytes => Self::Any(Uuid::from_slice(value_bytes)?), - }; - Ok(result) - } -} - -impl From> for ScheduleUid { - fn from(value: Vec) -> Self { - Self::try_from(value.as_slice()).expect("Failed to parse schedule uid from database") - } -} diff --git a/emgauwa-common/src/utils.rs b/emgauwa-common/src/utils.rs deleted file mode 100644 index 9b2d4ab..0000000 --- a/emgauwa-common/src/utils.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::ffi::CString; -use std::io::{Error, ErrorKind}; -use std::str::FromStr; - -use chrono::Datelike; -use log::LevelFilter; -use simple_logger::SimpleLogger; - -use crate::errors::EmgauwaError; -use crate::settings::Permissions; -use crate::types::{RelayStates, Weekday}; - - -pub fn init_logging(level: &str) -> Result<(), EmgauwaError> { - let log_level: LevelFilter = LevelFilter::from_str(level) - .map_err(|_| EmgauwaError::Other(format!("Invalid log level: {}", level)))?; - log::trace!("Log level set to {:?}", log_level); - - SimpleLogger::new() - .with_level(log_level) - .init() - .map_err(|err| EmgauwaError::Other(format!("Failed to initialize logger: {}", err)))?; - - Ok(()) -} - -// https://blog.lxsang.me/post/id/28.0 -pub fn drop_privileges(permissions: &Permissions) -> Result<(), Error> { - log::info!( - "Dropping privileges to {}:{}", - permissions.user, - permissions.group - ); - - // the group id need to be set first, because, when the user privileges drop, - // we are unable to drop the group privileges - if !permissions.group.is_empty() { - drop_privileges_group(&permissions.group)?; - } - if !permissions.user.is_empty() { - drop_privileges_user(&permissions.user)?; - } - Ok(()) -} - -fn drop_privileges_group(group: &str) -> Result<(), Error> { - // get the gid from username - if let Ok(cstr) = CString::new(group.as_bytes()) { - let p = unsafe { libc::getgrnam(cstr.as_ptr()) }; - if p.is_null() { - return Err(Error::new( - ErrorKind::Other, - format!("Unable to find group: {}", group), - )); - } - if unsafe { libc::setgid((*p).gr_gid) } != 0 { - return Err(Error::new( - ErrorKind::Other, - format!("Unable set gid for group: {}", group), - )); - } - } else { - return Err(Error::new( - ErrorKind::Other, - format!("Cannot create CString from groupname: {}", group), - )); - } - Ok(()) -} - -fn drop_privileges_user(user: &str) -> Result<(), Error> { - // get the uid from username - if let Ok(cstr) = CString::new(user.as_bytes()) { - let p = unsafe { libc::getpwnam(cstr.as_ptr()) }; - if p.is_null() { - return Err(Error::new( - ErrorKind::Other, - format!("Unable to find user: {}", user), - )); - } - if unsafe { libc::setuid((*p).pw_uid) } != 0 { - return Err(Error::new( - ErrorKind::Other, - format!("Unable set uid for user: {}", user), - )); - } - } else { - return Err(Error::new( - ErrorKind::Other, - format!("Cannot create CString from username: {}", user), - )); - } - Ok(()) -} - -pub fn get_weekday() -> Weekday { - (chrono::offset::Local::now() - .date_naive() - .weekday() - .number_from_monday() - - 1) as Weekday -} - -pub fn printable_relay_states(relay_states: &RelayStates) -> String { - let mut relay_debug = String::new(); - relay_states.iter().for_each(|state| { - relay_debug.push_str(match state { - Some(true) => "+", - Some(false) => "-", - None => "?", - }); - }); - relay_debug -} diff --git a/emgauwa-controller/Cargo.toml b/emgauwa-controller/Cargo.toml deleted file mode 100644 index 9947bc5..0000000 --- a/emgauwa-controller/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "emgauwa-controller" -version = "0.5.0" -edition = "2021" -authors = ["Tobias Reisinger "] - -[dependencies] -emgauwa-common = { path = "../emgauwa-common" } - -actix = "0.13" - -tokio = { version = "1.34", features = ["io-std", "macros", "rt-multi-thread"] } -tokio-tungstenite = "0.21" - -simple_logger = "4.3" -log = "0.4" - -chrono = { version = "0.4", features = ["serde"] } -uuid = { version = "1.5", features = ["serde", "v4"] } - -serde = "1.0" -serde_json = "1.0" -serde_derive = "1.0" - -sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] } - -futures = "0.3" -futures-channel = "0.3" - -rppal = "0.17" -rppal-pfd = "0.0.5" -rppal-mcp23s17 = "0.0.3" diff --git a/emgauwa-controller/src/app_state.rs b/emgauwa-controller/src/app_state.rs deleted file mode 100644 index ec9e262..0000000 --- a/emgauwa-controller/src/app_state.rs +++ /dev/null @@ -1,173 +0,0 @@ -use std::sync::Arc; -use std::time::{Duration, Instant}; - -use actix::{Actor, Context, Handler, Message}; -use emgauwa_common::constants; -use emgauwa_common::drivers::RelayDriver; -use emgauwa_common::errors::EmgauwaError; -use emgauwa_common::models::Controller; -use emgauwa_common::types::RelayStates; -use futures::executor::block_on; -use sqlx::{Pool, Sqlite}; -use tokio::sync::Notify; - -use crate::settings::Settings; - -#[derive(Message)] -#[rtype(result = "Result<(), EmgauwaError>")] -pub struct Reload {} - -#[derive(Message)] -#[rtype(result = "()")] -pub struct UpdateRelayStates { - pub relay_states: RelayStates, -} - -#[derive(Message)] -#[rtype(result = "Result<(), EmgauwaError>")] -pub struct RelayPulse { - pub relay_number: i64, - pub duration: Option, -} - -#[derive(Message)] -#[rtype(result = "Controller")] -pub struct GetThis {} - -#[derive(Message)] -#[rtype(result = "Arc")] -pub struct GetControllerNotifier {} - -#[derive(Message)] -#[rtype(result = "Arc")] -pub struct GetRelayNotifier {} - -pub struct AppState { - pub pool: Pool, - pub this: Controller, - pub settings: Settings, - pub drivers: Vec>, - pub controller_notifier: Arc, - pub relay_notifier: Arc, -} - -impl AppState { - pub fn new( - pool: Pool, - this: Controller, - settings: Settings, - drivers: Vec>, - ) -> AppState { - AppState { - pool, - this, - settings, - drivers, - controller_notifier: Arc::new(Notify::new()), - relay_notifier: Arc::new(Notify::new()), - } - } - - pub fn notify_controller_change(&self) { - self.controller_notifier.notify_one(); - } - - pub fn notify_relay_change(&self) { - self.relay_notifier.notify_one(); - } -} - -impl Actor for AppState { - type Context = Context; -} - -impl Handler for AppState { - type Result = Result<(), EmgauwaError>; - - fn handle(&mut self, _msg: Reload, _ctx: &mut Self::Context) -> Self::Result { - log::debug!("Reloading controller"); - let mut pool_conn = block_on(self.pool.acquire())?; - - self.this.reload(&mut pool_conn)?; - - self.notify_controller_change(); - - Ok(()) - } -} - -impl Handler for AppState { - type Result = (); - - fn handle(&mut self, msg: UpdateRelayStates, _ctx: &mut Self::Context) -> Self::Result { - self.this.apply_relay_states(&msg.relay_states); - self.drivers - .iter_mut() - .zip(msg.relay_states.iter()) - .for_each(|(driver, state)| { - if let Err(e) = driver.set(state.unwrap_or(false)) { - log::error!("Error setting relay: {}", e); - } - }); - - self.notify_relay_change(); - } -} - -impl Handler for AppState { - type Result = Result<(), EmgauwaError>; - - fn handle(&mut self, msg: RelayPulse, _ctx: &mut Self::Context) -> Self::Result { - let relay_num = msg.relay_number; - - let duration = Duration::from_secs( - match msg.duration { - None => { - self.settings - .get_relay(relay_num) - .ok_or(EmgauwaError::Other(String::from( - "Relay not found in settings", - )))? - .pulse - } - Some(dur) => Some(dur as u64), - } - .unwrap_or(constants::RELAY_PULSE_DURATION), - ); - let now = Instant::now(); - let until = now + duration; - - self.this.relay_pulse(relay_num, until)?; - log::debug!( - "Pulsing relay {} for {} seconds until {:?}", - relay_num, - duration.as_secs(), - until - ); - Ok(()) - } -} - -impl Handler for AppState { - type Result = Controller; - - fn handle(&mut self, _msg: GetThis, _ctx: &mut Self::Context) -> Self::Result { - self.this.clone() - } -} - -impl Handler for AppState { - type Result = Arc; - - fn handle(&mut self, _msg: GetControllerNotifier, _ctx: &mut Self::Context) -> Self::Result { - Arc::clone(&self.controller_notifier) - } -} - -impl Handler for AppState { - type Result = Arc; - - fn handle(&mut self, _msg: GetRelayNotifier, _ctx: &mut Self::Context) -> Self::Result { - Arc::clone(&self.relay_notifier) - } -} diff --git a/emgauwa-controller/src/driver.rs b/emgauwa-controller/src/driver.rs deleted file mode 100644 index 9df8d31..0000000 --- a/emgauwa-controller/src/driver.rs +++ /dev/null @@ -1,22 +0,0 @@ -use serde::{Deserialize, Deserializer}; - -#[derive(Debug, Clone, Copy)] -pub enum Driver { - Null, - Gpio, - PiFace, -} - -impl<'de> Deserialize<'de> for Driver { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - match String::deserialize(deserializer)?.as_str() { - "null" => Ok(Driver::Null), - "gpio" => Ok(Driver::Gpio), - "piface" => Ok(Driver::PiFace), - _ => Err(serde::de::Error::custom("invalid driver")), - } - } -} diff --git a/emgauwa-controller/src/main.rs b/emgauwa-controller/src/main.rs deleted file mode 100644 index 1ba2d42..0000000 --- a/emgauwa-controller/src/main.rs +++ /dev/null @@ -1,124 +0,0 @@ -use actix::Actor; -use emgauwa_common::db; -use emgauwa_common::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule}; -use emgauwa_common::errors::EmgauwaError; -use emgauwa_common::models::{Controller, FromDbModel}; -use emgauwa_common::types::EmgauwaUid; -use emgauwa_common::utils::{drop_privileges, init_logging}; -use rppal_pfd::PiFaceDigital; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::relay_loop::run_relays_loop; -use crate::settings::Settings; -use crate::ws::run_ws_loop; - -mod app_state; -mod driver; -mod relay_loop; -mod settings; -mod utils; -mod ws; - -async fn create_this_controller( - conn: &mut PoolConnection, - settings: &Settings, -) -> Result { - DbController::create( - conn, - &EmgauwaUid::default(), - &settings.name, - settings.relays.len() as i64, - ) - .await - .map_err(EmgauwaError::from) -} - -async fn create_this_relay( - conn: &mut PoolConnection, - this_controller: &DbController, - settings_relay: &settings::Relay, -) -> Result { - let relay = DbRelay::create( - conn, - &settings_relay.name, - settings_relay.number.ok_or(EmgauwaError::Internal( - "Relay number is missing".to_string(), - ))?, - this_controller, - ) - .await?; - - let off = DbSchedule::get_off(conn).await?; - for weekday in 0..7 { - DbJunctionRelaySchedule::set_schedule(conn, &relay, &off, weekday).await?; - } - - Ok(relay) -} - -#[actix::main] -async fn main() -> Result<(), std::io::Error> { - let settings = settings::init()?; - - drop_privileges(&settings.permissions)?; - - init_logging(&settings.logging.level)?; - - let mut pfd: Option = None; - let drivers = settings.relays_make_drivers(&mut pfd)?; - - let pool = db::init(&settings.database) - .await - .map_err(EmgauwaError::from)?; - - let mut conn = pool.acquire().await.map_err(EmgauwaError::from)?; - - let db_controller = match DbController::get_all(&mut conn) - .await - .map_err(EmgauwaError::from)? - .pop() - { - None => futures::executor::block_on(create_this_controller(&mut conn, &settings))?, - Some(c) => c, - }; - - for relay in &settings.relays { - if DbRelay::get_by_controller_and_num( - &mut conn, - &db_controller, - relay.number.ok_or(EmgauwaError::Internal( - "Relay number is missing".to_string(), - ))?, - ) - .await - .map_err(EmgauwaError::from)? - .is_none() - { - create_this_relay(&mut conn, &db_controller, relay) - .await - .map_err(EmgauwaError::from)?; - } - } - - let db_controller = db_controller - .update(&mut conn, &db_controller.name, settings.relays.len() as i64) - .await - .map_err(EmgauwaError::from)?; - - let this = Controller::from_db_model(&mut conn, db_controller).map_err(EmgauwaError::from)?; - - let url = format!( - "ws://{}:{}/api/v1/ws/controllers", - settings.server.host, settings.server.port - ); - - let app_state = app_state::AppState::new(pool.clone(), this, settings, drivers).start(); - - let _ = tokio::join!( - tokio::spawn(run_relays_loop(app_state.clone())), - tokio::spawn(run_ws_loop(pool.clone(), app_state.clone(), url)), - ); - - Ok(()) -} diff --git a/emgauwa-controller/src/relay_loop.rs b/emgauwa-controller/src/relay_loop.rs deleted file mode 100644 index 3247f7f..0000000 --- a/emgauwa-controller/src/relay_loop.rs +++ /dev/null @@ -1,150 +0,0 @@ -use std::time::{Duration, Instant}; - -use actix::Addr; -use chrono::{Local, Timelike}; -use emgauwa_common::constants::RELAYS_RETRY_TIMEOUT; -use emgauwa_common::errors::EmgauwaError; -use emgauwa_common::models::Controller; -use emgauwa_common::types::{RelayStates, Weekday}; -use emgauwa_common::utils::printable_relay_states; -use futures::pin_mut; -use tokio::time; -use tokio::time::timeout; -use utils::app_state_get_controller_notifier; - -use crate::app_state::AppState; -use crate::utils; - -pub async fn run_relays_loop(app_state: Addr) { - log::debug!("Spawned relays loop"); - loop { - let run_result = run_relays(&app_state).await; - if let Err(err) = run_result { - log::error!("Error running relays: {}", err); - } - time::sleep(RELAYS_RETRY_TIMEOUT).await; - } -} - -async fn run_relays(app_state: &Addr) -> Result<(), EmgauwaError> { - let notifier = &*app_state_get_controller_notifier(app_state).await?; - - let mut last_weekday = emgauwa_common::utils::get_weekday(); - let mut this = utils::app_state_get_this(app_state).await?; - let mut relay_states: RelayStates = Vec::new(); - init_relay_states(&mut relay_states, &this); - calc_relay_states(&mut relay_states, &mut this, app_state).await?; - - let mut duration_override = None; - - loop { - log::debug!( - "Relay loop at {}: {}", - Local::now().naive_local().time(), - printable_relay_states(&this.get_relay_states()) - ); - - let notifier_future = notifier.notified(); - pin_mut!(notifier_future); - let mut changed = timeout( - get_next_duration(&this, &mut duration_override), - &mut notifier_future, - ) - .await - .is_ok(); - - check_weekday(app_state, &mut last_weekday, &mut changed).await?; - - if changed { - log::debug!("Reloading controller in relay loop"); - this = utils::app_state_get_this(app_state).await?; - } - - let now_pulse = Instant::now(); - duration_override = this - .relays - .iter_mut() - .filter_map(|relay| match relay.check_pulsing(&now_pulse) { - None => None, - Some(pulse) => { - let dur = pulse - now_pulse; - log::debug!( - "Pulsing relay {} for {}s until {:?} ", - relay.r.number, - dur.as_secs(), - pulse - ); - Some(dur) - } - }) - .min(); - - calc_relay_states(&mut relay_states, &mut this, app_state).await?; - } -} - -fn init_relay_states(relay_states: &mut RelayStates, this: &Controller) { - relay_states.clear(); - for _ in 0..this.c.relay_count { - relay_states.push(None); - } -} - -async fn calc_relay_states( - relay_states: &mut RelayStates, - this: &mut Controller, - app_state: &Addr, -) -> Result<(), EmgauwaError> { - let now = Local::now().time(); - let now_pulse = Instant::now(); - - this.relays - .iter_mut() - .zip(relay_states.iter_mut()) - .for_each(|(relay, state)| { - relay.is_on = Some( - relay.active_schedule.is_on(&now) || relay.check_pulsing(&now_pulse).is_some(), - ); - *state = relay.is_on; - }); - utils::app_state_update_relays_on(app_state, relay_states.clone()).await -} - -fn get_next_duration(this: &Controller, duration_override: &mut Option) -> Duration { - if let Some(duration) = duration_override { - log::debug!("Duration override. Waiting for {}s", duration.as_secs()); - return *duration; - } - - let now = Local::now().time(); - let now_in_s = now.num_seconds_from_midnight(); - let next_timestamp = this - .get_next_time(&now) - .map_or(86400, |t| t.num_seconds_from_midnight()); - - let duration_to_next = Duration::from_secs((next_timestamp - now_in_s) as u64); - - log::debug!( - "Next timestamp: {}; Waiting for {}s", - next_timestamp, - duration_to_next.as_secs() - ); - - duration_to_next -} - -async fn check_weekday( - app_state: &Addr, - last_weekday: &mut Weekday, - changed: &mut bool, -) -> Result<(), EmgauwaError> { - let current_weekday = emgauwa_common::utils::get_weekday(); - if current_weekday.ne(last_weekday) { - log::debug!("Weekday changed"); - *last_weekday = current_weekday; - utils::app_state_reload(app_state).await?; - *changed = true; - } - - Ok(()) -} diff --git a/emgauwa-controller/src/settings.rs b/emgauwa-controller/src/settings.rs deleted file mode 100644 index 0e6562a..0000000 --- a/emgauwa-controller/src/settings.rs +++ /dev/null @@ -1,106 +0,0 @@ -use emgauwa_common::errors::EmgauwaError; -use emgauwa_common::{drivers, settings}; -use rppal_pfd::PiFaceDigital; -use serde_derive::Deserialize; - -use crate::driver::Driver; - -#[derive(Clone, Debug, Deserialize)] -#[serde(default)] -#[allow(unused)] -pub struct Relay { - pub driver: Driver, - pub name: String, - pub number: Option, - pub pin: u8, - pub inverted: bool, - pub pulse: Option, -} - -#[derive(Clone, Debug, Deserialize)] -#[serde(default)] -#[allow(unused)] -pub struct Settings { - pub server: settings::Server, - pub database: String, - pub permissions: settings::Permissions, - pub logging: settings::Logging, - - pub name: String, - pub relays: Vec, -} - -impl Default for Settings { - fn default() -> Self { - Settings { - server: settings::Server::default(), - database: String::from("sqlite://emgauwa-controller.sqlite"), - permissions: settings::Permissions::default(), - logging: settings::Logging::default(), - - name: String::from("Emgauwa Controller"), - relays: Vec::new(), - } - } -} - -impl Default for Relay { - fn default() -> Self { - Relay { - driver: Driver::Gpio, - number: None, - name: String::from("Relay"), - pin: 0, - inverted: false, - pulse: None, - } - } -} - -pub fn init() -> Result { - let mut settings: Settings = settings::load("controller", "CONTROLLER")?; - - for (num, relay) in settings.relays.iter_mut().enumerate() { - if relay.number.is_none() { - relay.number = Some(num as i64); - } - } - - Ok(settings) -} - -impl Settings { - pub fn get_relay(&self, number: i64) -> Option<&Relay> { - self.relays.iter().find(|r| r.number == Some(number)) - } - - pub fn relays_make_drivers( - &self, - pfd: &mut Option, - ) -> Result>, EmgauwaError> { - let mut drivers = Vec::new(); - for relay in &self.relays { - drivers.push(relay.make_driver(pfd)?); - } - Ok(drivers) - } -} - -impl Relay { - pub fn make_driver( - &self, - pfd: &mut Option, - ) -> Result, EmgauwaError> { - let driver: Box = match self.driver { - Driver::Null => Box::new(drivers::NullDriver::new(self.pin)), - Driver::Gpio => Box::new(drivers::GpioDriver::new(self.pin, self.inverted)?), - Driver::PiFace => { - if pfd.is_none() { - *pfd = Some(drivers::PiFaceDriver::init_piface()?); - } - Box::new(drivers::PiFaceDriver::new(self.pin, pfd)?) - } - }; - Ok(driver) - } -} diff --git a/emgauwa-controller/src/utils.rs b/emgauwa-controller/src/utils.rs deleted file mode 100644 index c6d2830..0000000 --- a/emgauwa-controller/src/utils.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::sync::Arc; - -use actix::Addr; -use emgauwa_common::errors::EmgauwaError; -use emgauwa_common::models::Controller; -use emgauwa_common::types::RelayStates; -use tokio::sync::Notify; - -use crate::app_state; -use crate::app_state::AppState; - -pub async fn app_state_get_this(app_state: &Addr) -> Result { - app_state - .send(app_state::GetThis {}) - .await - .map_err(EmgauwaError::from) -} - -pub async fn app_state_get_relay_notifier( - app_state: &Addr, -) -> Result, EmgauwaError> { - app_state - .send(app_state::GetRelayNotifier {}) - .await - .map_err(EmgauwaError::from) -} - -pub async fn app_state_get_controller_notifier( - app_state: &Addr, -) -> Result, EmgauwaError> { - app_state - .send(app_state::GetControllerNotifier {}) - .await - .map_err(EmgauwaError::from) -} - -pub async fn app_state_reload(app_state: &Addr) -> Result<(), EmgauwaError> { - app_state - .send(app_state::Reload {}) - .await - .map_err(EmgauwaError::from)? -} - -pub async fn app_state_update_relays_on( - app_state: &Addr, - relay_states: RelayStates, -) -> Result<(), EmgauwaError> { - app_state - .send(app_state::UpdateRelayStates { relay_states }) - .await - .map_err(EmgauwaError::from) -} - -pub async fn app_state_relay_pulse( - app_state: &Addr, - relay_number: i64, - duration: Option, -) -> Result<(), EmgauwaError> { - app_state - .send(app_state::RelayPulse { - relay_number, - duration, - }) - .await - .map_err(EmgauwaError::from)? -} diff --git a/emgauwa-controller/src/ws/mod.rs b/emgauwa-controller/src/ws/mod.rs deleted file mode 100644 index 994cec6..0000000 --- a/emgauwa-controller/src/ws/mod.rs +++ /dev/null @@ -1,247 +0,0 @@ -use actix::Addr; -use emgauwa_common::constants::WEBSOCKET_RETRY_TIMEOUT; -use emgauwa_common::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule}; -use emgauwa_common::errors::{DatabaseError, EmgauwaError}; -use emgauwa_common::models::{Controller, Relay}; -use emgauwa_common::types::{ControllerWsAction, ScheduleUid}; -use futures::{future, pin_mut, SinkExt, StreamExt}; -use sqlx::pool::PoolConnection; -use sqlx::{Pool, Sqlite}; -use tokio::time; -use tokio_tungstenite::tungstenite::Message; -use tokio_tungstenite::{connect_async, tungstenite}; - -use crate::app_state::AppState; -use crate::utils; -use crate::utils::{app_state_get_relay_notifier, app_state_get_this}; - -pub async fn run_ws_loop(pool: Pool, app_state: Addr, url: String) { - log::debug!("Spawned ws loop"); - loop { - let run_result = run_websocket(pool.clone(), &app_state, &url).await; - if let Err(err) = run_result { - log::error!("Error running websocket: {}", err); - } - - log::info!( - "Retrying to connect in {} seconds...", - WEBSOCKET_RETRY_TIMEOUT.as_secs() - ); - time::sleep(WEBSOCKET_RETRY_TIMEOUT).await; - } -} - -async fn run_websocket( - pool: Pool, - app_state: &Addr, - url: &str, -) -> Result<(), EmgauwaError> { - log::debug!("Trying to connect to {}", url); - match connect_async(url).await { - Ok(connection) => { - log::info!("Websocket connected"); - let (ws_stream, _) = connection; - - let (mut write, read) = ws_stream.split(); - - let ws_action = ControllerWsAction::Register(app_state_get_this(app_state).await?); - - let ws_action_json = serde_json::to_string(&ws_action)?; - if let Err(err) = write.send(Message::text(ws_action_json)).await { - log::error!("Failed to register at websocket: {}", err); - return Ok(()); - } - - let (app_state_tx, app_state_rx) = futures_channel::mpsc::unbounded::(); - tokio::spawn(read_app_state(app_state.clone(), app_state_tx)); - let app_state_to_ws = app_state_rx.map(Ok).forward(write); - - let read_handler = read.for_each(|msg| handle_message(pool.clone(), app_state, msg)); - - pin_mut!(app_state_to_ws, read_handler); - future::select(app_state_to_ws, read_handler).await; - - log::warn!("Lost connection to websocket"); - } - Err(err) => { - log::warn!("Failed to connect to websocket: {}", err,); - } - } - Ok(()) -} - -async fn read_app_state( - app_state: Addr, - tx: futures_channel::mpsc::UnboundedSender, -) -> Result<(), EmgauwaError> { - let notifier = &*app_state_get_relay_notifier(&app_state).await?; - loop { - notifier.notified().await; - log::debug!("Relay change detected"); - let this = app_state_get_this(&app_state).await?; - let relay_states = this.get_relay_states(); - let ws_action = ControllerWsAction::RelayStates((this.c.uid, relay_states)); - - let ws_action_json = serde_json::to_string(&ws_action)?; - tx.unbounded_send(Message::text(ws_action_json)) - .map_err(|_| { - EmgauwaError::Other(String::from( - "Failed to forward message from app state to websocket", - )) - })?; - } -} - -async fn handle_message( - pool: Pool, - app_state: &Addr, - message_result: Result, -) { - let msg = match message_result { - Ok(msg) => msg, - Err(err) => { - log::error!("Error reading message: {}", err); - return; - } - }; - if let Message::Text(text) = msg { - match serde_json::from_str(&text) { - Ok(action) => { - log::debug!("Received action: {:?}", action); - let mut pool_conn = match pool.acquire().await { - Ok(conn) => conn, - Err(err) => { - log::error!("Failed to acquire database connection: {:?}", err); - return; - } - }; - let action_res = handle_action(&mut pool_conn, app_state, action).await; - if let Err(e) = action_res { - log::error!("Error handling action: {:?}", e); - } - } - Err(e) => { - log::error!("Error deserializing action: {:?}", e); - } - } - } -} - -pub async fn handle_action( - conn: &mut PoolConnection, - app_state: &Addr, - action: ControllerWsAction, -) -> Result<(), EmgauwaError> { - let this = app_state_get_this(app_state).await?; - - match action { - ControllerWsAction::Controller(controller) => { - handle_controller(conn, &this, controller).await? - } - ControllerWsAction::Relays(relays) => handle_relays(conn, &this, relays).await?, - ControllerWsAction::Schedules(schedules) => handle_schedules(conn, schedules).await?, - ControllerWsAction::RelayPulse((relay_num, duration)) => { - handle_relay_pulse(app_state, relay_num, duration).await? - } - _ => return Ok(()), - }; - - utils::app_state_reload(app_state).await -} - -async fn handle_controller( - conn: &mut PoolConnection, - this: &Controller, - controller: Controller, -) -> Result<(), EmgauwaError> { - if controller.c.uid != this.c.uid { - return Err(EmgauwaError::Other(String::from( - "Controller UID mismatch during update", - ))); - } - DbController::get_by_uid(conn, &controller.c.uid) - .await? - .ok_or(DatabaseError::NotFound)? - .update(conn, controller.c.name.as_str(), this.c.relay_count) - .await?; - - Ok(()) -} - -async fn handle_schedules( - conn: &mut PoolConnection, - schedules: Vec, -) -> Result<(), EmgauwaError> { - let mut handled_uids = vec![ - // on and off schedules are always present and should not be updated - ScheduleUid::On, - ScheduleUid::Off, - ]; - for schedule in schedules { - if handled_uids.contains(&schedule.uid) { - continue; - } - handled_uids.push(schedule.uid.clone()); - - log::debug!("Handling schedule: {:?}", schedule); - let schedule_db = DbSchedule::get_by_uid(conn, &schedule.uid).await?; - - if let Some(schedule_db) = schedule_db { - schedule_db - .update(conn, schedule.name.as_str(), &schedule.periods) - .await?; - } else { - DbSchedule::create( - conn, - schedule.uid.clone(), - schedule.name.as_str(), - &schedule.periods, - ) - .await?; - } - } - - Ok(()) -} - -async fn handle_relays( - conn: &mut PoolConnection, - this: &Controller, - relays: Vec, -) -> Result<(), EmgauwaError> { - for relay in relays { - if relay.controller.uid != this.c.uid { - return Err(EmgauwaError::Other(String::from( - "Controller UID mismatch during relay update", - ))); - } - let db_relay = DbRelay::get_by_controller_and_num(conn, &this.c, relay.r.number) - .await? - .ok_or(DatabaseError::NotFound)?; - - db_relay.update(conn, relay.r.name.as_str()).await?; - - handle_schedules(conn, relay.schedules.clone()).await?; - - let mut schedules = Vec::new(); // We need to get the schedules from the database to have the right IDs - for schedule in relay.schedules { - schedules.push( - DbSchedule::get_by_uid(conn, &schedule.uid) - .await? - .ok_or(DatabaseError::NotFound)?, - ); - } - - DbJunctionRelaySchedule::set_schedules(conn, &db_relay, schedules.iter().collect()).await?; - } - - Ok(()) -} - -async fn handle_relay_pulse( - app_state: &Addr, - relay_num: i64, - duration: Option, -) -> Result<(), EmgauwaError> { - utils::app_state_relay_pulse(app_state, relay_num, duration).await -} diff --git a/emgauwa-core/Cargo.toml b/emgauwa-core/Cargo.toml deleted file mode 100644 index 29d5a12..0000000 --- a/emgauwa-core/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "emgauwa-core" -version = "0.5.0" -edition = "2021" -authors = ["Tobias Reisinger "] - -[dependencies] -emgauwa-common = { path = "../emgauwa-common" } - -actix = "0.13" -actix-web = "4.4" -actix-web-actors = "4.2" -actix-cors = "0.7" - -utoipa = "4.2" -utoipa-swagger-ui = { version = "6.0", features = ["actix-web", "debug-embed"] } - -log = "0.4" - -chrono = { version = "0.4", features = ["serde"] } -uuid = { version = "1.5", features = ["serde", "v4"] } -itertools = "0.12" - -serde = "1.0" -serde_json = "1.0" -serde_derive = "1.0" - -sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] } - -futures = "0.3" -tokio = { version = "1.36", features = ["rt", "rt-multi-thread"] } diff --git a/migrations/20231120000000_init.down.sql b/migrations/20231120000000_init.down.sql deleted file mode 100644 index 2547da8..0000000 --- a/migrations/20231120000000_init.down.sql +++ /dev/null @@ -1,8 +0,0 @@ -DROP TABLE macro_actions; -DROP TABLE macros; -DROP TABLE junction_relay_schedule; -DROP TABLE junction_tag; -DROP TABLE tags; -DROP TABLE schedules; -DROP TABLE relays; -DROP TABLE controllers; diff --git a/migrations/20231120000000_init.up.sql b/migrations/20231120000000_init.up.sql deleted file mode 100644 index 6c588eb..0000000 --- a/migrations/20231120000000_init.up.sql +++ /dev/null @@ -1,161 +0,0 @@ -CREATE TABLE controllers -( - id - INTEGER - PRIMARY KEY - AUTOINCREMENT - NOT NULL, - uid - BLOB - NOT NULL - UNIQUE, - name - VARCHAR(128) - NOT NULL, - relay_count - INTEGER - NOT NULL, - active - BOOLEAN - NOT NULL - DEFAULT false -); - -CREATE TABLE relays -( - id - INTEGER - PRIMARY KEY - AUTOINCREMENT - NOT NULL, - name - VARCHAR(128) - NOT NULL, - number - INTEGER - NOT NULL, - controller_id - INTEGER - NOT NULL - REFERENCES controllers (id) - ON DELETE CASCADE -); - -CREATE TABLE schedules -( - id - INTEGER - PRIMARY KEY - AUTOINCREMENT - NOT NULL, - uid - BLOB - NOT NULL - UNIQUE, - name - VARCHAR(128) - NOT NULL, - periods - BLOB - NOT NULL -); - -CREATE TABLE tags -( - id - INTEGER - PRIMARY KEY - AUTOINCREMENT - NOT NULL, - tag - VARCHAR(128) - NOT NULL - UNIQUE -); - -CREATE TABLE junction_tag -( - id - INTEGER - PRIMARY KEY - AUTOINCREMENT - NOT NULL, - tag_id - INTEGER - NOT NULL - REFERENCES tags (id) - ON DELETE CASCADE, - relay_id - INTEGER - REFERENCES relays (id) - ON DELETE CASCADE, - schedule_id - INTEGER - REFERENCES schedules (id) - ON DELETE CASCADE -); - -CREATE TABLE junction_relay_schedule -( - id - INTEGER - PRIMARY KEY - AUTOINCREMENT - NOT NULL, - weekday - SMALLINT - NOT NULL, - relay_id - INTEGER - NOT NULL - REFERENCES relays (id) - ON DELETE CASCADE, - schedule_id - INTEGER - NOT NULL - REFERENCES schedules (id) - ON DELETE SET DEFAULT -); - -CREATE TABLE macros -( - id - INTEGER - PRIMARY KEY - AUTOINCREMENT - NOT NULL, - uid - BLOB - NOT NULL - UNIQUE, - name - VARCHAR(128) - NOT NULL -); - -CREATE TABLE macro_actions -( - id - INTEGER - PRIMARY KEY - AUTOINCREMENT - NOT NULL, - macro_id - INTEGER - NOT NULL - REFERENCES macros (id) - ON DELETE CASCADE, - relay_id - INTEGER - NOT NULL - REFERENCES relays (id) - ON DELETE CASCADE, - schedule_id - INTEGER - NOT NULL - REFERENCES schedules (id) - ON DELETE CASCADE, - weekday - SMALLINT - NOT NULL -); diff --git a/emgauwa-core/src/app_state.rs b/src/app_state.rs similarity index 100% rename from emgauwa-core/src/app_state.rs rename to src/app_state.rs diff --git a/emgauwa-core/src/handlers/mod.rs b/src/handlers/mod.rs similarity index 100% rename from emgauwa-core/src/handlers/mod.rs rename to src/handlers/mod.rs diff --git a/emgauwa-core/src/handlers/v1/controllers.rs b/src/handlers/v1/controllers.rs similarity index 100% rename from emgauwa-core/src/handlers/v1/controllers.rs rename to src/handlers/v1/controllers.rs diff --git a/emgauwa-core/src/handlers/v1/macros.rs b/src/handlers/v1/macros.rs similarity index 100% rename from emgauwa-core/src/handlers/v1/macros.rs rename to src/handlers/v1/macros.rs diff --git a/emgauwa-core/src/handlers/v1/mod.rs b/src/handlers/v1/mod.rs similarity index 100% rename from emgauwa-core/src/handlers/v1/mod.rs rename to src/handlers/v1/mod.rs diff --git a/emgauwa-core/src/handlers/v1/relays.rs b/src/handlers/v1/relays.rs similarity index 100% rename from emgauwa-core/src/handlers/v1/relays.rs rename to src/handlers/v1/relays.rs diff --git a/emgauwa-core/src/handlers/v1/schedules.rs b/src/handlers/v1/schedules.rs similarity index 99% rename from emgauwa-core/src/handlers/v1/schedules.rs rename to src/handlers/v1/schedules.rs index 7c8ccc6..8e329a1 100644 --- a/emgauwa-core/src/handlers/v1/schedules.rs +++ b/src/handlers/v1/schedules.rs @@ -171,7 +171,6 @@ pub async fn update( .await??; } - let return_schedule = Schedule::from_db_model(&mut pool_conn, schedule)?; Ok(HttpResponse::Ok().json(return_schedule)) } diff --git a/emgauwa-core/src/handlers/v1/tags.rs b/src/handlers/v1/tags.rs similarity index 100% rename from emgauwa-core/src/handlers/v1/tags.rs rename to src/handlers/v1/tags.rs diff --git a/emgauwa-core/src/handlers/v1/ws/controllers/handlers.rs b/src/handlers/v1/ws/controllers/handlers.rs similarity index 100% rename from emgauwa-core/src/handlers/v1/ws/controllers/handlers.rs rename to src/handlers/v1/ws/controllers/handlers.rs diff --git a/emgauwa-core/src/handlers/v1/ws/controllers/mod.rs b/src/handlers/v1/ws/controllers/mod.rs similarity index 100% rename from emgauwa-core/src/handlers/v1/ws/controllers/mod.rs rename to src/handlers/v1/ws/controllers/mod.rs diff --git a/emgauwa-core/src/handlers/v1/ws/mod.rs b/src/handlers/v1/ws/mod.rs similarity index 100% rename from emgauwa-core/src/handlers/v1/ws/mod.rs rename to src/handlers/v1/ws/mod.rs diff --git a/emgauwa-core/src/handlers/v1/ws/relays/mod.rs b/src/handlers/v1/ws/relays/mod.rs similarity index 100% rename from emgauwa-core/src/handlers/v1/ws/relays/mod.rs rename to src/handlers/v1/ws/relays/mod.rs diff --git a/emgauwa-core/src/main.rs b/src/main.rs similarity index 100% rename from emgauwa-core/src/main.rs rename to src/main.rs diff --git a/emgauwa-core/src/settings.rs b/src/settings.rs similarity index 100% rename from emgauwa-core/src/settings.rs rename to src/settings.rs diff --git a/emgauwa-core/src/utils.rs b/src/utils.rs similarity index 100% rename from emgauwa-core/src/utils.rs rename to src/utils.rs