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 ce5073b..0000000 Binary files a/.sqlx/query-03bfe5bd71d673a621b6ad6af5f71bf18ede519e6b950933f6751ef9cf7521c9.json and /dev/null differ diff --git a/.sqlx/query-0874e71c6206efc82528cc450bc86549c43e9be2f4ac3b257e836187fa9ed080.json b/.sqlx/query-0874e71c6206efc82528cc450bc86549c43e9be2f4ac3b257e836187fa9ed080.json deleted file mode 100644 index 9b1f908..0000000 Binary files a/.sqlx/query-0874e71c6206efc82528cc450bc86549c43e9be2f4ac3b257e836187fa9ed080.json and /dev/null differ diff --git a/.sqlx/query-08c517120fcfb4534a3ff540910417afca55278e269f12203f4fc83096944810.json b/.sqlx/query-08c517120fcfb4534a3ff540910417afca55278e269f12203f4fc83096944810.json deleted file mode 100644 index b29819b..0000000 Binary files a/.sqlx/query-08c517120fcfb4534a3ff540910417afca55278e269f12203f4fc83096944810.json and /dev/null differ diff --git a/.sqlx/query-1348af1c13719ffcd72c2a4c6712b2bab2a9923c715295d8b1d937d33844d1a4.json b/.sqlx/query-1348af1c13719ffcd72c2a4c6712b2bab2a9923c715295d8b1d937d33844d1a4.json deleted file mode 100644 index f7313fd..0000000 Binary files a/.sqlx/query-1348af1c13719ffcd72c2a4c6712b2bab2a9923c715295d8b1d937d33844d1a4.json and /dev/null differ diff --git a/.sqlx/query-1cb4cc57ff361d6d84a0c8e8f5df1ff46abbb21dfc3eb3f4f38b251f99d10abd.json b/.sqlx/query-1cb4cc57ff361d6d84a0c8e8f5df1ff46abbb21dfc3eb3f4f38b251f99d10abd.json deleted file mode 100644 index 5be1688..0000000 Binary files a/.sqlx/query-1cb4cc57ff361d6d84a0c8e8f5df1ff46abbb21dfc3eb3f4f38b251f99d10abd.json and /dev/null differ diff --git a/.sqlx/query-1eda8cf54e553e8e892ac63a31cb94e91e7851a53ebae17a26b19300b83d7dac.json b/.sqlx/query-1eda8cf54e553e8e892ac63a31cb94e91e7851a53ebae17a26b19300b83d7dac.json deleted file mode 100644 index e3769c9..0000000 Binary files a/.sqlx/query-1eda8cf54e553e8e892ac63a31cb94e91e7851a53ebae17a26b19300b83d7dac.json and /dev/null differ diff --git a/.sqlx/query-20e99a281e5e3e9c9d7375425d93d15ccf840e9f469007d37b6eb47f96f31dc7.json b/.sqlx/query-20e99a281e5e3e9c9d7375425d93d15ccf840e9f469007d37b6eb47f96f31dc7.json deleted file mode 100644 index 776039b..0000000 Binary files a/.sqlx/query-20e99a281e5e3e9c9d7375425d93d15ccf840e9f469007d37b6eb47f96f31dc7.json and /dev/null differ diff --git a/.sqlx/query-2551c285e3e223311cff8e32022d8b11e95d56b2f166326301a0b6722fc1fd44.json b/.sqlx/query-2551c285e3e223311cff8e32022d8b11e95d56b2f166326301a0b6722fc1fd44.json deleted file mode 100644 index f6f2724..0000000 Binary files a/.sqlx/query-2551c285e3e223311cff8e32022d8b11e95d56b2f166326301a0b6722fc1fd44.json and /dev/null differ diff --git a/.sqlx/query-2e3528a386066c3fc11be1259298f281279353d0b92625c46ef5cd09a672c031.json b/.sqlx/query-2e3528a386066c3fc11be1259298f281279353d0b92625c46ef5cd09a672c031.json deleted file mode 100644 index fe474ae..0000000 Binary files a/.sqlx/query-2e3528a386066c3fc11be1259298f281279353d0b92625c46ef5cd09a672c031.json and /dev/null differ diff --git a/.sqlx/query-3adb7ab9cacd5a84d882e4c9860be83d32bb0380b1fb76f90bf4e200636c7f6a.json b/.sqlx/query-3adb7ab9cacd5a84d882e4c9860be83d32bb0380b1fb76f90bf4e200636c7f6a.json deleted file mode 100644 index 59ce879..0000000 Binary files a/.sqlx/query-3adb7ab9cacd5a84d882e4c9860be83d32bb0380b1fb76f90bf4e200636c7f6a.json and /dev/null differ diff --git a/.sqlx/query-3b5a701d0ace12125c573680bcd5d9768b3709b347afb04c329b8fb1d3972111.json b/.sqlx/query-3b5a701d0ace12125c573680bcd5d9768b3709b347afb04c329b8fb1d3972111.json deleted file mode 100644 index 390622e..0000000 Binary files a/.sqlx/query-3b5a701d0ace12125c573680bcd5d9768b3709b347afb04c329b8fb1d3972111.json and /dev/null differ diff --git a/.sqlx/query-3fe383ea9ed4965e25d54eea08fb5abbab1b0c8eec7cf11597267de780299d0d.json b/.sqlx/query-3fe383ea9ed4965e25d54eea08fb5abbab1b0c8eec7cf11597267de780299d0d.json deleted file mode 100644 index 4b85000..0000000 Binary files a/.sqlx/query-3fe383ea9ed4965e25d54eea08fb5abbab1b0c8eec7cf11597267de780299d0d.json and /dev/null differ diff --git a/.sqlx/query-457e9d4808332255ed7354a28e6ebc2015558f66dbcd00cb4ce559d80f2bd023.json b/.sqlx/query-457e9d4808332255ed7354a28e6ebc2015558f66dbcd00cb4ce559d80f2bd023.json deleted file mode 100644 index e40bb94..0000000 Binary files a/.sqlx/query-457e9d4808332255ed7354a28e6ebc2015558f66dbcd00cb4ce559d80f2bd023.json and /dev/null differ diff --git a/.sqlx/query-486ed307f718754a3d4ea2c6fe944f9571c88fad3316593edf0c72ddef24c73e.json b/.sqlx/query-486ed307f718754a3d4ea2c6fe944f9571c88fad3316593edf0c72ddef24c73e.json deleted file mode 100644 index a4bfa26..0000000 Binary files a/.sqlx/query-486ed307f718754a3d4ea2c6fe944f9571c88fad3316593edf0c72ddef24c73e.json and /dev/null differ diff --git a/.sqlx/query-49afbeaa1d32f8c2e6fea7a2d57a11ec04dd0aab26139c19583af00e5baaba56.json b/.sqlx/query-49afbeaa1d32f8c2e6fea7a2d57a11ec04dd0aab26139c19583af00e5baaba56.json deleted file mode 100644 index 90e1cca..0000000 Binary files a/.sqlx/query-49afbeaa1d32f8c2e6fea7a2d57a11ec04dd0aab26139c19583af00e5baaba56.json and /dev/null differ diff --git a/.sqlx/query-4f5408e64f5e6a8dd923c3b147f993ce9e4cafc90204b06977481130ec06d111.json b/.sqlx/query-4f5408e64f5e6a8dd923c3b147f993ce9e4cafc90204b06977481130ec06d111.json deleted file mode 100644 index 78e4867..0000000 Binary files a/.sqlx/query-4f5408e64f5e6a8dd923c3b147f993ce9e4cafc90204b06977481130ec06d111.json and /dev/null differ diff --git a/.sqlx/query-52958684fa52b7a4753cd4356482dc6c655102a501d8aa48e03b1fb3dbbad02d.json b/.sqlx/query-52958684fa52b7a4753cd4356482dc6c655102a501d8aa48e03b1fb3dbbad02d.json deleted file mode 100644 index 63080c4..0000000 Binary files a/.sqlx/query-52958684fa52b7a4753cd4356482dc6c655102a501d8aa48e03b1fb3dbbad02d.json and /dev/null differ diff --git a/.sqlx/query-5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465.json b/.sqlx/query-5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465.json deleted file mode 100644 index 67ea7e8..0000000 Binary files a/.sqlx/query-5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465.json and /dev/null differ diff --git a/.sqlx/query-66a141b71041a7827f1932e6e288fdab37cc699720e5484c30697b5566b8d513.json b/.sqlx/query-66a141b71041a7827f1932e6e288fdab37cc699720e5484c30697b5566b8d513.json deleted file mode 100644 index c08e9fc..0000000 Binary files a/.sqlx/query-66a141b71041a7827f1932e6e288fdab37cc699720e5484c30697b5566b8d513.json and /dev/null differ diff --git a/.sqlx/query-711398eb64710a25cc818167be8f3b2d03cab39116c2732c06e3c74a02fb0367.json b/.sqlx/query-711398eb64710a25cc818167be8f3b2d03cab39116c2732c06e3c74a02fb0367.json deleted file mode 100644 index 4409006..0000000 Binary files a/.sqlx/query-711398eb64710a25cc818167be8f3b2d03cab39116c2732c06e3c74a02fb0367.json and /dev/null differ diff --git a/.sqlx/query-7519da166e2e0b6de4c02559fc173396e85db762d482607e4662e788c5542fea.json b/.sqlx/query-7519da166e2e0b6de4c02559fc173396e85db762d482607e4662e788c5542fea.json deleted file mode 100644 index 2640d90..0000000 Binary files a/.sqlx/query-7519da166e2e0b6de4c02559fc173396e85db762d482607e4662e788c5542fea.json and /dev/null differ diff --git a/.sqlx/query-772c710d542c7f5aaa49069fe12a7a37ef509f50954509557ec985c996960da2.json b/.sqlx/query-772c710d542c7f5aaa49069fe12a7a37ef509f50954509557ec985c996960da2.json deleted file mode 100644 index 6915ce7..0000000 Binary files a/.sqlx/query-772c710d542c7f5aaa49069fe12a7a37ef509f50954509557ec985c996960da2.json and /dev/null differ diff --git a/.sqlx/query-7b3ee1cad84a146699b9010be6d31665a416687772b95d159e5d3e98cde3511c.json b/.sqlx/query-7b3ee1cad84a146699b9010be6d31665a416687772b95d159e5d3e98cde3511c.json deleted file mode 100644 index c904161..0000000 Binary files a/.sqlx/query-7b3ee1cad84a146699b9010be6d31665a416687772b95d159e5d3e98cde3511c.json and /dev/null differ diff --git a/.sqlx/query-7bbe1a982c77194feba2ff610b01e824b99a3888adf5365619ecfed7ae6544a9.json b/.sqlx/query-7bbe1a982c77194feba2ff610b01e824b99a3888adf5365619ecfed7ae6544a9.json deleted file mode 100644 index 217c70f..0000000 Binary files a/.sqlx/query-7bbe1a982c77194feba2ff610b01e824b99a3888adf5365619ecfed7ae6544a9.json and /dev/null differ diff --git a/.sqlx/query-7cd5b42013b4e6a37a670e55cd3ceb0911001eee41256f7b324edc72f5cadcba.json b/.sqlx/query-7cd5b42013b4e6a37a670e55cd3ceb0911001eee41256f7b324edc72f5cadcba.json deleted file mode 100644 index 8d846ef..0000000 Binary files a/.sqlx/query-7cd5b42013b4e6a37a670e55cd3ceb0911001eee41256f7b324edc72f5cadcba.json and /dev/null differ diff --git a/.sqlx/query-7e7cdf2650c08feb10a35275e693d0d3c690b4e9bccc6f0f5fed2bfd2826b480.json b/.sqlx/query-7e7cdf2650c08feb10a35275e693d0d3c690b4e9bccc6f0f5fed2bfd2826b480.json deleted file mode 100644 index c9df19d..0000000 Binary files a/.sqlx/query-7e7cdf2650c08feb10a35275e693d0d3c690b4e9bccc6f0f5fed2bfd2826b480.json and /dev/null differ diff --git a/.sqlx/query-87b7396fb761030e25836cb90ed07a1bdaa1d82ae3d43d107cf8bac3e9ba4c25.json b/.sqlx/query-87b7396fb761030e25836cb90ed07a1bdaa1d82ae3d43d107cf8bac3e9ba4c25.json deleted file mode 100644 index 63d4cfd..0000000 Binary files a/.sqlx/query-87b7396fb761030e25836cb90ed07a1bdaa1d82ae3d43d107cf8bac3e9ba4c25.json and /dev/null differ diff --git a/.sqlx/query-8bdd41a11fd2ca0440b2d6c0dc752a342012048741bdcd1ff9461bc46e1cf701.json b/.sqlx/query-8bdd41a11fd2ca0440b2d6c0dc752a342012048741bdcd1ff9461bc46e1cf701.json deleted file mode 100644 index 39b331c..0000000 Binary files a/.sqlx/query-8bdd41a11fd2ca0440b2d6c0dc752a342012048741bdcd1ff9461bc46e1cf701.json and /dev/null differ diff --git a/.sqlx/query-90ed2cd2f8161552dae06ab2d9cfee8914e253db00e6a648a78e99ade6a4de60.json b/.sqlx/query-90ed2cd2f8161552dae06ab2d9cfee8914e253db00e6a648a78e99ade6a4de60.json deleted file mode 100644 index 9be1170..0000000 Binary files a/.sqlx/query-90ed2cd2f8161552dae06ab2d9cfee8914e253db00e6a648a78e99ade6a4de60.json and /dev/null differ diff --git a/.sqlx/query-921a0775f75d9e2f67f604265872bf715af1e1ed4d420ac6fa132a94ff352e56.json b/.sqlx/query-921a0775f75d9e2f67f604265872bf715af1e1ed4d420ac6fa132a94ff352e56.json deleted file mode 100644 index a7c8c1e..0000000 Binary files a/.sqlx/query-921a0775f75d9e2f67f604265872bf715af1e1ed4d420ac6fa132a94ff352e56.json and /dev/null differ diff --git a/.sqlx/query-96a0b9960daa8a10e04e22cba592870941545b095193612956c29d37a5a1b774.json b/.sqlx/query-96a0b9960daa8a10e04e22cba592870941545b095193612956c29d37a5a1b774.json deleted file mode 100644 index c86b729..0000000 Binary files a/.sqlx/query-96a0b9960daa8a10e04e22cba592870941545b095193612956c29d37a5a1b774.json and /dev/null differ diff --git a/.sqlx/query-96f34b8654265ea5ab5210ab5dcad8c0bacd8d9e73e375dc35e759bdb82369a1.json b/.sqlx/query-96f34b8654265ea5ab5210ab5dcad8c0bacd8d9e73e375dc35e759bdb82369a1.json deleted file mode 100644 index 705a3b0..0000000 Binary files a/.sqlx/query-96f34b8654265ea5ab5210ab5dcad8c0bacd8d9e73e375dc35e759bdb82369a1.json and /dev/null differ diff --git a/.sqlx/query-a0c9c1a108c6560b4f073c866415a94af2e823a8d88ab2aa5ace8c2b56023004.json b/.sqlx/query-a0c9c1a108c6560b4f073c866415a94af2e823a8d88ab2aa5ace8c2b56023004.json deleted file mode 100644 index ead136f..0000000 Binary files a/.sqlx/query-a0c9c1a108c6560b4f073c866415a94af2e823a8d88ab2aa5ace8c2b56023004.json and /dev/null differ diff --git a/.sqlx/query-a1f5699889cfabb3f681e6bb71a8d7c739d0cb82eea1f8289d012c37bd30c776.json b/.sqlx/query-a1f5699889cfabb3f681e6bb71a8d7c739d0cb82eea1f8289d012c37bd30c776.json deleted file mode 100644 index 2dfa516..0000000 Binary files a/.sqlx/query-a1f5699889cfabb3f681e6bb71a8d7c739d0cb82eea1f8289d012c37bd30c776.json and /dev/null differ diff --git a/.sqlx/query-a64694ec1a81472a05a68b1caf64c32a261bb7d1f34577c0f4dc52e318124ecd.json b/.sqlx/query-a64694ec1a81472a05a68b1caf64c32a261bb7d1f34577c0f4dc52e318124ecd.json deleted file mode 100644 index cd86c5f..0000000 Binary files a/.sqlx/query-a64694ec1a81472a05a68b1caf64c32a261bb7d1f34577c0f4dc52e318124ecd.json and /dev/null differ diff --git a/.sqlx/query-a69f0bf9b3fefd7914502aa15fa120ecf4e5542011b781d8023bdb422580d97a.json b/.sqlx/query-a69f0bf9b3fefd7914502aa15fa120ecf4e5542011b781d8023bdb422580d97a.json deleted file mode 100644 index f6c5009..0000000 Binary files a/.sqlx/query-a69f0bf9b3fefd7914502aa15fa120ecf4e5542011b781d8023bdb422580d97a.json and /dev/null differ diff --git a/.sqlx/query-a6dc153657cb3fefb5ba5b763dc5b7b4da78ab8acec79c7e7ea51ce5c9414bcb.json b/.sqlx/query-a6dc153657cb3fefb5ba5b763dc5b7b4da78ab8acec79c7e7ea51ce5c9414bcb.json deleted file mode 100644 index a0e8505..0000000 Binary files a/.sqlx/query-a6dc153657cb3fefb5ba5b763dc5b7b4da78ab8acec79c7e7ea51ce5c9414bcb.json and /dev/null differ diff --git a/.sqlx/query-a8b2d9cfd386b5f9ad5b76ef08711691dae057a431fffc27417e4f5504dcfb30.json b/.sqlx/query-a8b2d9cfd386b5f9ad5b76ef08711691dae057a431fffc27417e4f5504dcfb30.json deleted file mode 100644 index bd4f9f7..0000000 Binary files a/.sqlx/query-a8b2d9cfd386b5f9ad5b76ef08711691dae057a431fffc27417e4f5504dcfb30.json and /dev/null differ diff --git a/.sqlx/query-a94cb95af7e6c13e0e155c74de2febe44c1250f25989034a3f9e4e8bcb39dece.json b/.sqlx/query-a94cb95af7e6c13e0e155c74de2febe44c1250f25989034a3f9e4e8bcb39dece.json deleted file mode 100644 index b203d50..0000000 Binary files a/.sqlx/query-a94cb95af7e6c13e0e155c74de2febe44c1250f25989034a3f9e4e8bcb39dece.json and /dev/null differ diff --git a/.sqlx/query-ab8dafa95af67dc06074e83e1ec61be2fe713637f883d175f9b279f0f2f8fd87.json b/.sqlx/query-ab8dafa95af67dc06074e83e1ec61be2fe713637f883d175f9b279f0f2f8fd87.json deleted file mode 100644 index 24cf02e..0000000 Binary files a/.sqlx/query-ab8dafa95af67dc06074e83e1ec61be2fe713637f883d175f9b279f0f2f8fd87.json and /dev/null differ diff --git a/.sqlx/query-b41855e635ac409559fa63cba4c1285034c573b86e3193da3995606dee412153.json b/.sqlx/query-b41855e635ac409559fa63cba4c1285034c573b86e3193da3995606dee412153.json deleted file mode 100644 index 94419bd..0000000 Binary files a/.sqlx/query-b41855e635ac409559fa63cba4c1285034c573b86e3193da3995606dee412153.json and /dev/null differ diff --git a/.sqlx/query-b6f6fd898dc3f1dbe2c39bf0445bac76c8233c0feee15f4504bf74ea864716ce.json b/.sqlx/query-b6f6fd898dc3f1dbe2c39bf0445bac76c8233c0feee15f4504bf74ea864716ce.json deleted file mode 100644 index 611b0de..0000000 Binary files a/.sqlx/query-b6f6fd898dc3f1dbe2c39bf0445bac76c8233c0feee15f4504bf74ea864716ce.json and /dev/null differ diff --git a/.sqlx/query-b91f6aab0bdb316633b3a0d75303086dd6e4e204e9e063535b19db666476fe88.json b/.sqlx/query-b91f6aab0bdb316633b3a0d75303086dd6e4e204e9e063535b19db666476fe88.json deleted file mode 100644 index 24b6a13..0000000 Binary files a/.sqlx/query-b91f6aab0bdb316633b3a0d75303086dd6e4e204e9e063535b19db666476fe88.json and /dev/null differ diff --git a/.sqlx/query-bcd9d4dd3641e6c84262ed5f6c5646f825be186da276b113c0150aaad26b057c.json b/.sqlx/query-bcd9d4dd3641e6c84262ed5f6c5646f825be186da276b113c0150aaad26b057c.json deleted file mode 100644 index b312064..0000000 Binary files a/.sqlx/query-bcd9d4dd3641e6c84262ed5f6c5646f825be186da276b113c0150aaad26b057c.json and /dev/null differ diff --git a/.sqlx/query-c138a9c659a7410e9935ad3f6a56c2bc73174fb1921fe1260702f1eab87d979c.json b/.sqlx/query-c138a9c659a7410e9935ad3f6a56c2bc73174fb1921fe1260702f1eab87d979c.json deleted file mode 100644 index 6518f69..0000000 Binary files a/.sqlx/query-c138a9c659a7410e9935ad3f6a56c2bc73174fb1921fe1260702f1eab87d979c.json and /dev/null differ diff --git a/.sqlx/query-c30156fb112fcc28f08fbbec04197c41c9f71f6a4a3f44221f5ec012c99ebf54.json b/.sqlx/query-c30156fb112fcc28f08fbbec04197c41c9f71f6a4a3f44221f5ec012c99ebf54.json deleted file mode 100644 index 9d225d6..0000000 Binary files a/.sqlx/query-c30156fb112fcc28f08fbbec04197c41c9f71f6a4a3f44221f5ec012c99ebf54.json and /dev/null differ diff --git a/.sqlx/query-c9437ff0c3014b269dcb21304fbad12237b9cb69ea6aa4686df6d5262065faa2.json b/.sqlx/query-c9437ff0c3014b269dcb21304fbad12237b9cb69ea6aa4686df6d5262065faa2.json deleted file mode 100644 index 10fe1a3..0000000 Binary files a/.sqlx/query-c9437ff0c3014b269dcb21304fbad12237b9cb69ea6aa4686df6d5262065faa2.json and /dev/null differ diff --git a/.sqlx/query-cb0d76a3a1cfa439d48056c865cd5bfbfbc785fa795254120822b843ce63ff07.json b/.sqlx/query-cb0d76a3a1cfa439d48056c865cd5bfbfbc785fa795254120822b843ce63ff07.json deleted file mode 100644 index 0626f8d..0000000 Binary files a/.sqlx/query-cb0d76a3a1cfa439d48056c865cd5bfbfbc785fa795254120822b843ce63ff07.json and /dev/null differ diff --git a/.sqlx/query-cbda6fd5137f3698537262772df22a0a66064e3416e4f5c89d8d83fa6c0536ad.json b/.sqlx/query-cbda6fd5137f3698537262772df22a0a66064e3416e4f5c89d8d83fa6c0536ad.json deleted file mode 100644 index fb83516..0000000 Binary files a/.sqlx/query-cbda6fd5137f3698537262772df22a0a66064e3416e4f5c89d8d83fa6c0536ad.json and /dev/null differ diff --git a/.sqlx/query-cdcae4768f7b62390e5e5da850e301b502ffd56dd88485cd73b694f6559ccd4e.json b/.sqlx/query-cdcae4768f7b62390e5e5da850e301b502ffd56dd88485cd73b694f6559ccd4e.json deleted file mode 100644 index 97b4b65..0000000 Binary files a/.sqlx/query-cdcae4768f7b62390e5e5da850e301b502ffd56dd88485cd73b694f6559ccd4e.json and /dev/null differ diff --git a/.sqlx/query-db48fb93e9f22ee7da0786ae913d962b1da35b5b2b663f759f6af106650951b8.json b/.sqlx/query-db48fb93e9f22ee7da0786ae913d962b1da35b5b2b663f759f6af106650951b8.json deleted file mode 100644 index f31d95b..0000000 Binary files a/.sqlx/query-db48fb93e9f22ee7da0786ae913d962b1da35b5b2b663f759f6af106650951b8.json and /dev/null differ diff --git a/.sqlx/query-e9386ab7ecbe4ce13f8ee5ee5852697741fb210ea8bfc3d61f1508106c0b076e.json b/.sqlx/query-e9386ab7ecbe4ce13f8ee5ee5852697741fb210ea8bfc3d61f1508106c0b076e.json deleted file mode 100644 index e54e4a6..0000000 Binary files a/.sqlx/query-e9386ab7ecbe4ce13f8ee5ee5852697741fb210ea8bfc3d61f1508106c0b076e.json and /dev/null differ diff --git a/.sqlx/query-e94ef5bc8b267d493375bb371dcfb7b09f6355ecbc8b6e1085d5f2f9a08cac3f.json b/.sqlx/query-e94ef5bc8b267d493375bb371dcfb7b09f6355ecbc8b6e1085d5f2f9a08cac3f.json deleted file mode 100644 index 391ca72..0000000 Binary files a/.sqlx/query-e94ef5bc8b267d493375bb371dcfb7b09f6355ecbc8b6e1085d5f2f9a08cac3f.json and /dev/null differ diff --git a/.sqlx/query-ea4b06aaad9436096e20a53d81fd36ed21da18ceed5b71e51a1e749bab466422.json b/.sqlx/query-ea4b06aaad9436096e20a53d81fd36ed21da18ceed5b71e51a1e749bab466422.json deleted file mode 100644 index 1967a26..0000000 Binary files a/.sqlx/query-ea4b06aaad9436096e20a53d81fd36ed21da18ceed5b71e51a1e749bab466422.json and /dev/null differ diff --git a/.sqlx/query-ee7da56331bece2efe21b55dbd5f420d3abb08358a1abe301dc7e08693fbef4d.json b/.sqlx/query-ee7da56331bece2efe21b55dbd5f420d3abb08358a1abe301dc7e08693fbef4d.json deleted file mode 100644 index c4ec08d..0000000 Binary files a/.sqlx/query-ee7da56331bece2efe21b55dbd5f420d3abb08358a1abe301dc7e08693fbef4d.json and /dev/null differ diff --git a/.sqlx/query-eee8820f59927c1560a5fd461b9f4a2d40abdf3843d0c815399ea9d267cade5a.json b/.sqlx/query-eee8820f59927c1560a5fd461b9f4a2d40abdf3843d0c815399ea9d267cade5a.json deleted file mode 100644 index cb7c1bd..0000000 Binary files a/.sqlx/query-eee8820f59927c1560a5fd461b9f4a2d40abdf3843d0c815399ea9d267cade5a.json and /dev/null differ diff --git a/.sqlx/query-f75318134ec1facc3de6b04232cb28a92524b8f556840b76f76c8f317059e668.json b/.sqlx/query-f75318134ec1facc3de6b04232cb28a92524b8f556840b76f76c8f317059e668.json deleted file mode 100644 index 88dc7d1..0000000 Binary files a/.sqlx/query-f75318134ec1facc3de6b04232cb28a92524b8f556840b76f76c8f317059e668.json and /dev/null differ diff --git a/.sqlx/query-f7b90a6a0af3f7d7c96158f96f1ada06827a0e5a2d2dbd48bfb8e04f973a6131.json b/.sqlx/query-f7b90a6a0af3f7d7c96158f96f1ada06827a0e5a2d2dbd48bfb8e04f973a6131.json deleted file mode 100644 index caa30a4..0000000 Binary files a/.sqlx/query-f7b90a6a0af3f7d7c96158f96f1ada06827a0e5a2d2dbd48bfb8e04f973a6131.json and /dev/null differ diff --git a/.sqlx/query-fde0dca1aba5b490a9f5f6006677be750a7bcf5d1185935e8386671c69dc7270.json b/.sqlx/query-fde0dca1aba5b490a9f5f6006677be750a7bcf5d1185935e8386671c69dc7270.json deleted file mode 100644 index 8d164fb..0000000 Binary files a/.sqlx/query-fde0dca1aba5b490a9f5f6006677be750a7bcf5d1185935e8386671c69dc7270.json and /dev/null differ diff --git a/Cargo.toml b/Cargo.toml index 92bf0a0..9ca77d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,32 @@ -[workspace] -resolver = "2" -members = [ - "emgauwa-core", - "emgauwa-controller", - "emgauwa-common", -] +[package] +name = "emgauwa-controller" +version = "0.5.0" +edition = "2021" +authors = ["Tobias Reisinger "] + +[dependencies] +emgauwa-common = { git = "https://git.serguzim.me/emgauwa/common.git" } + +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/Cross.toml b/Cross.toml deleted file mode 100644 index 2b8c82d..0000000 --- a/Cross.toml +++ /dev/null @@ -1,5 +0,0 @@ -[build] -pre-build = [ - "curl -Lo /usr/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64", - "chmod +x /usr/bin/yq" -] diff --git a/Makefile b/Makefile index 7c49652..ba15c15 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-controller.json: controller.pkl pkl eval -f json -o $@ $< -configs: - $(MAKE) emgauwa-core.json +config: $(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-controller_arm-unknown-linux-gnueabihf + $(MAKE) TOOL=cargo emgauwa-controller_x86_64-unknown-linux-gnu + $(MAKE) TOOL=cross emgauwa-controller_x86_64-unknown-linux-musl diff --git a/api.v1.yaml b/api.v1.yaml deleted file mode 100644 index 9b362a3..0000000 --- a/api.v1.yaml +++ /dev/null @@ -1,875 +0,0 @@ -openapi: 3.0.0 -info: - contact: - name: Tobias Reisinger - url: 'https://git.serguzim.me/emgauwa/' - title: Emgauwa API v1 - version: 0.5.0 - description: Server API to manage an Emgauwa system. -servers: - - url: 'http://localhost:4419' -tags: - - name: schedules - - name: relays - - name: controllers - - name: tags - - name: macros - - name: websocket -paths: - /api/v1/schedules: - get: - summary: get all schedules - description: Receive a list with all available schedules. - tags: - - schedules - responses: - '200': - description: OK - headers: { } - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/schedule' - operationId: get-api-v1-schedules - post: - summary: add new schedule - tags: - - schedules - responses: - '201': - description: Created - content: - application/json: - schema: - $ref: '#/components/schemas/schedule' - operationId: post-api-v1-schedules - description: Create a new schedule. A new unique id will be returned - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/schedule' - description: The "id" field will be set by the server. - parameters: [ ] - '/api/v1/schedules/{schedule_id}': - parameters: - - schema: - type: string - format: uuid - name: schedule_id - in: path - required: true - description: '' - get: - summary: get single schedule - tags: - - schedules - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/schedule' - '404': - description: Not Found - content: - application/json: - schema: - type: object - properties: { } - operationId: get-schedules-schedule_id - description: Return a single schedule by id. - put: - summary: overwrite single schedule - tags: - - schedules - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/schedule' - '400': - description: Bad Request - content: - application/json: - schema: - type: object - properties: { } - '404': - description: Not Found - content: - application/json: - schema: - type: object - properties: { } - operationId: put-schedules-schedule_id - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - type: string - periods: - type: array - items: - $ref: '#/components/schemas/period' - tags: - type: array - items: - $ref: '#/components/schemas/tag' - description: '' - parameters: [ ] - description: Overwrite the properties for a single schedule. Overwriting periods on "on" or "off" will fail. - delete: - summary: delete single schedule - tags: - - schedules - responses: - '200': - description: OK - '403': - description: Forbidden - '404': - description: Not Found - operationId: delete-schedules-schedule_id - description: Deletes a single schedule. Deleting "on" or "off" is forbidden (403). - '/api/v1/schedules/tag/{tag}': - parameters: - - schema: - type: string - name: tag - in: path - required: true - description: '' - get: - summary: get schedules by tag - tags: - - schedules - responses: - '200': - description: OK - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/schedule' - operationId: get-schedules-tag-schedule_id - description: Receive a list of schedules which include the given tag. - /api/v1/relays: - get: - summary: get all relays - tags: - - relays - responses: - '200': - description: OK - headers: { } - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/relay' - operationId: get-relays - description: Return a list with all relays. - parameters: [ ] - '/api/v1/relays/tag/{tag}': - parameters: - - schema: - type: string - name: tag - in: path - required: true - get: - summary: get relays by tag - tags: - - relays - responses: - '200': - description: OK - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/relay' - operationId: get-relays-tag-tag - description: Return all relays with the given tag. - /api/v1/controllers: - get: - summary: get all controllers - tags: - - controllers - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/controller' - operationId: get-controllers - description: Return all controllers. - parameters: [ ] - '/api/v1/controllers/{controller_id}': - parameters: - - schema: - type: string - name: controller_id - in: path - description: '' - required: true - get: - summary: get single controller - tags: - - controllers - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/controller' - '404': - description: Not Found - content: - application/json: - schema: - type: object - properties: { } - operationId: get-controllers-controller_id - requestBody: - content: - application/json: - schema: - type: object - properties: { } - description: '' - description: Return a single controller by id. When no controller with the id is found 404 will be returned. - put: - summary: overwrite single controller - tags: - - controllers - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/controller' - '400': - description: Bad Request - content: - application/json: - schema: - type: object - properties: { } - '404': - description: Not Found - content: - application/json: - schema: - type: object - properties: { } - operationId: put-controllers-controller_id - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - type: string - ip: - type: string - format: ipv4 - description: Overwrite properties of a single controller. - delete: - summary: delete single controller - tags: - - controllers - responses: - '200': - description: OK - '404': - description: Not Found - operationId: delete-controllers-controller_id - description: Delete a single controller. To recover the controller you need to use the controllers/discover feature. - '/api/v1/controllers/{controller_id}/relays': - parameters: - - schema: - type: string - name: controller_id - in: path - required: true - get: - summary: get all relays for single controller - tags: - - controllers - - relays - responses: - '200': - description: OK - headers: { } - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/relay' - '404': - description: Not Found - content: - application/json: - schema: - type: array - items: { } - operationId: get-controllers-controller_id-relays - description: Returns all relays for a single controller. - '/api/v1/controllers/{controller_id}/relays/{relay_num}': - parameters: - - schema: - type: string - name: controller_id - in: path - required: true - - schema: - type: integer - name: relay_num - in: path - required: true - get: - summary: get single relay for single controller - tags: - - controllers - - relays - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/relay' - '404': - description: Not Found - content: - application/json: - schema: - type: object - properties: { } - operationId: get-controllers-controller_id-relays-relay_num - description: 'Return a single relay by number for a controller by id. When the relay or controller is not found, 404 will be returned.' - put: - summary: overwrite single relay for single controller - tags: - - controllers - - relays - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/relay' - '400': - description: Bad Request - content: - application/json: - schema: - type: object - properties: { } - '404': - description: Not Found - content: - application/json: - schema: - type: object - properties: { } - operationId: put-controllers-controller_id-relays-relay_num - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - type: string - active_schedule: - type: object - properties: - id: - $ref: '#/components/schemas/schedule_id' - schedules: - type: array - maxItems: 7 - minItems: 7 - items: - type: object - properties: - id: - $ref: '#/components/schemas/schedule_id' - tags: - type: array - items: - $ref: '#/components/schemas/tag' - description: 'active schedule will overwrite schedules[weekday]' - /api/v1/tags: - get: - summary: get all tags - tags: - - tags - responses: - '200': - description: OK - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/tag' - operationId: get-tags - description: Returns a list of tags. - parameters: [ ] - post: - summary: add new tag - operationId: post-api-v1-tags - responses: - '201': - description: Created - content: - application/json: - schema: - $ref: '#/components/schemas/tag_full' - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - tag: - $ref: '#/components/schemas/tag' - description: '' - tags: - - tags - description: Add a new tag. Will return 400 when the tag already exits. - '/api/v1/tags/{tag}': - parameters: - - schema: - type: string - name: tag - in: path - required: true - get: - summary: get relays and schedules for tag - tags: - - tags - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/tag_full' - '404': - description: Not Found - operationId: get-tags-tag - description: Return all models with the given tag (relays and schedules) - delete: - summary: delete tag - operationId: delete-tags-tag - responses: - '200': - description: OK - '404': - description: Not Found - description: delete tag from database and from affected relays and schedules - tags: - - tags - '/api/v1/controllers/{controller_id}/relays/{relay_num}/pulse': - parameters: - - schema: - type: string - name: controller_id - in: path - required: true - - schema: - type: string - name: relay_num - in: path - required: true - post: - summary: pulse relay on - responses: - '200': - description: OK - '404': - description: Not Found - operationId: post-controllers-controller_id-relays-relay_num-pulse - requestBody: - content: - application/json: - schema: - type: object - properties: - duration: - type: integer - description: '' - description: Turn a relay on for a short amount of time. The duration can be set in the body in seconds. When no duration is supplied the default for the relay will be used. The default is read from the controller's config. - tags: - - controllers - - relays - /api/v1/ws/relays: - get: - summary: get relay status updates - tags: - - websocket - responses: - '200': - description: OK - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/relay' - operationId: get-ws-relays - description: |- - WEBSOCKET - This websocket will send all relays with the most recent status every 10 seconds. - parameters: [ ] - /api/v1/macros: - get: - summary: get all macros - tags: - - macros - responses: - '200': - description: OK - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/macro' - operationId: get-api-v1-macros - description: Receive a list with all available macros. - post: - summary: add new macro - tags: - - macros - responses: - '201': - description: Created - operationId: post-api-v1-macros - description: Create a new macro. A new unique id will be returned - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - type: string - actions: - type: array - items: - type: object - properties: - weekday: - type: integer - minimum: 0 - maximum: 6 - relay: - type: object - properties: - number: - type: integer - controller_id: - $ref: '#/components/schemas/controller_id' - schedule: - type: object - properties: - id: - $ref: '#/components/schemas/schedule_id' - '/api/v1/macros/{macro_id}': - parameters: - - schema: - type: string - name: macro_id - in: path - required: true - get: - summary: get a single macro - tags: - - macros - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/macro' - operationId: get-api-v1-macros-macro_id - description: Return a single macro by id. When no macro with the id is found 404 will be returned. - put: - summary: overwrite a macro - tags: - - macros - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/macro' - operationId: put-api-v1-macros-macro_id - description: Overwrite properties of a single macro. - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - type: string - actions: - type: array - items: - type: object - properties: - weekday: - type: integer - minimum: 0 - maximum: 6 - schedule: - type: object - properties: - id: - $ref: '#/components/schemas/schedule_id' - relay: - type: object - properties: - number: - type: integer - controller_id: - $ref: '#/components/schemas/controller_id' - delete: - summary: delete a macro - tags: - - macros - responses: - '200': - description: OK - operationId: delete-api-v1-macros-macro_id - description: Delete a single macro. - '/api/v1/macros/{macro_id}/execute': - parameters: - - schema: - type: string - name: macro_id - in: path - required: true - - schema: - type: integer - name: weekday - in: query - put: - summary: execute a macro - tags: - - macros - responses: - '200': - description: OK - '404': - description: Not Found - operationId: put-api-v1-macros-macro_id-execute - description: Execute a macro - /api/v1/schedules/list: - post: - summary: add new schedule list - tags: - - schedules - responses: - '200': - description: OK - operationId: post-schedules-list - requestBody: - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/schedule' - description: Create a list of schedules - parameters: [ ] -components: - schemas: - controller: - title: controller - type: object - properties: - id: - $ref: '#/components/schemas/controller_id' - name: - type: string - example: Garden Controller - ip: - type: string - format: ipv4 - example: 224.73.153.12 - active: - type: boolean - port: - type: integer - example: 27480 - relay_count: - type: integer - minimum: 0 - example: 10 - relays: - type: array - items: - $ref: '#/components/schemas/relay' - relay: - title: relay - type: object - properties: - number: - type: integer - minimum: 0 - example: 3 - name: - type: string - example: Sprinkling System 1 - controller_id: - $ref: '#/components/schemas/controller_id' - active_schedule: - $ref: '#/components/schemas/schedule-untagged' - schedules: - type: array - maxItems: 7 - minItems: 7 - items: - $ref: '#/components/schemas/schedule-untagged' - tags: - type: array - items: - $ref: '#/components/schemas/tag' - is_on: - type: boolean - description: NULL when unknown - schedule-untagged: - title: schedule - type: object - description: '' - properties: - id: - $ref: '#/components/schemas/schedule_id' - name: - type: string - example: Sprinkler Sunny Day - periods: - type: array - items: - $ref: '#/components/schemas/period' - schedule: - title: schedule - type: object - description: '' - properties: - id: - $ref: '#/components/schemas/schedule_id' - name: - type: string - example: Sprinkler Sunny Day - periods: - type: array - items: - $ref: '#/components/schemas/period' - tags: - type: array - items: - $ref: '#/components/schemas/tag' - period: - title: period - type: object - properties: - start: - type: string - example: '10:15' - format: 24-hour - end: - type: string - format: 24-hour - example: '14:45' - required: - - start - - end - controller_id: - type: string - title: controller_id - format: uuid - example: 589c0eab-a4b4-4f3a-be97-cf03b1dc8edc - tag: - type: string - title: tag - example: sprinkler - tag_full: - title: tag (full) - type: object - properties: - tag: - $ref: '#/components/schemas/tag' - relays: - type: array - items: - $ref: '#/components/schemas/relay' - schedules: - type: array - items: - $ref: '#/components/schemas/schedule' - schedule_id: - type: string - title: schedule_id - format: uuid - example: 6bceb29b-7d2e-4af3-a26e-11f514dc5cc1 - macro: - title: macro - type: object - properties: - id: - type: string - format: uuid - example: a9a4eab4-6c54-4fe4-b755-bdb2a90b3242 - name: - type: string - actions: - type: array - items: - $ref: '#/components/schemas/macro_action' - macro_action: - title: macro_action - type: object - description: '' - properties: - weekday: - type: integer - minimum: 0 - maximum: 6 - schedule: - $ref: '#/components/schemas/schedule' - relay: - $ref: '#/components/schemas/relay' - required: - - weekday - - schedule - - relay diff --git a/config/core.pkl b/config/core.pkl deleted file mode 100644 index 92c294a..0000000 --- a/config/core.pkl +++ /dev/null @@ -1 +0,0 @@ -amends "package://emgauwa.app/pkl/emgauwa@0.1.0#/core.pkl" diff --git a/config/controller.pkl b/controller.pkl similarity index 100% rename from config/controller.pkl rename to controller.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-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/emgauwa-core/build.rs b/emgauwa-core/build.rs deleted file mode 100644 index 27e067f..0000000 --- a/emgauwa-core/build.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::process::{exit, Command}; - -fn main() { - 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") - .output() - .expect("Failed to convert api documentation to json"); - - if !output.status.success() { - eprintln!("Error: {}", String::from_utf8_lossy(&output.stderr)); - exit(1); - } -} diff --git a/emgauwa-core/src/app_state.rs b/emgauwa-core/src/app_state.rs deleted file mode 100644 index 840fd1b..0000000 --- a/emgauwa-core/src/app_state.rs +++ /dev/null @@ -1,188 +0,0 @@ -use std::collections::HashMap; - -use actix::{Actor, Addr, Context, Handler, Message, Recipient}; -use emgauwa_common::db::DbController; -use emgauwa_common::errors::EmgauwaError; -use emgauwa_common::models::{convert_db_list, Controller, Relay}; -use emgauwa_common::types::{ControllerWsAction, EmgauwaUid, RelayStates}; -use futures::executor::block_on; -use sqlx::{Pool, Sqlite}; - -use crate::handlers::v1::ws::relays::{RelaysWs, SendRelays}; - -#[derive(Message)] -#[rtype(result = "Result<(), EmgauwaError>")] -pub struct DisconnectController { - pub controller_uid: EmgauwaUid, -} - -#[derive(Message)] -#[rtype(result = "Result<(), EmgauwaError>")] -pub struct ConnectController { - pub address: Recipient, - pub controller: Controller, -} - -#[derive(Message)] -#[rtype(result = "()")] -pub struct UpdateRelayStates { - pub controller_uid: EmgauwaUid, - pub relay_states: RelayStates, -} - -#[derive(Message)] -#[rtype(result = "Result, EmgauwaError>")] -pub struct GetRelays {} - -#[derive(Message)] -#[rtype(result = "Result<(), EmgauwaError>")] -pub struct Action { - pub controller_uid: EmgauwaUid, - pub action: ControllerWsAction, -} - -#[derive(Message)] -#[rtype(result = "()")] -pub struct ConnectRelayClient { - pub addr: Addr, -} - -pub struct AppState { - pub pool: Pool, - pub connected_controllers: HashMap)>, - pub connected_relay_clients: Vec>, -} - -impl AppState { - pub fn new(pool: Pool) -> AppState { - AppState { - pool, - connected_controllers: HashMap::new(), - connected_relay_clients: Vec::new(), - } - } - - fn get_relays(&self) -> Result, EmgauwaError> { - let mut pool_conn = block_on(self.pool.acquire())?; - let db_controllers = block_on(DbController::get_all(&mut pool_conn))?; - let mut controllers: Vec = convert_db_list(&mut pool_conn, db_controllers)?; - - self.connected_controllers - .iter() - .for_each(|(uid, (connected_controller, _))| { - if let Some(c) = controllers.iter_mut().find(|c| c.c.uid == *uid) { - c.apply_relay_states(&connected_controller.get_relay_states()); - } - }); - - let mut relays: Vec = Vec::new(); - controllers.iter().for_each(|c| { - relays.extend(c.relays.clone()); - }); - - Ok(relays) - } - - fn notify_relay_clients(&mut self) { - self.connected_relay_clients.retain(|addr| addr.connected()); - - match self.get_relays() { - Ok(relays) => match serde_json::to_string(&relays) { - Ok(json) => { - self.connected_relay_clients.iter_mut().for_each(|addr| { - let relays_json = json.clone(); - addr.do_send(SendRelays { relays_json }); - }); - } - Err(err) => { - log::error!("Failed to serialize relays: {:?}", err); - } - }, - Err(err) => { - log::error!("Failed to get relays: {:?}", err); - } - }; - } -} - -impl Actor for AppState { - type Context = Context; -} - -impl Handler for AppState { - type Result = Result<(), EmgauwaError>; - - fn handle(&mut self, msg: DisconnectController, _ctx: &mut Self::Context) -> Self::Result { - let mut pool_conn = block_on(self.pool.acquire())?; - - if let Some((controller, address)) = self.connected_controllers.remove(&msg.controller_uid) - { - if let Err(err) = block_on(controller.c.update_active(&mut pool_conn, false)) { - log::error!( - "Failed to mark controller {} as inactive: {:?}", - controller.c.uid, - err - ); - } - // TODO: why does the block_on(send()) version not return? The AppState will be stuck. - //block_on(address.send(ControllerWsAction::Disconnect))??; - address.do_send(ControllerWsAction::Disconnect); - } - Ok(()) - } -} - -impl Handler for AppState { - type Result = Result<(), EmgauwaError>; - - fn handle(&mut self, msg: ConnectController, _ctx: &mut Self::Context) -> Self::Result { - log::debug!("Connecting controller: {}", msg.controller.c.uid); - self.connected_controllers - .insert(msg.controller.c.uid.clone(), (msg.controller, msg.address)); - - Ok(()) - } -} - -impl Handler for AppState { - type Result = (); - - fn handle(&mut self, msg: UpdateRelayStates, _ctx: &mut Self::Context) -> Self::Result { - if let Some((controller, _)) = self.connected_controllers.get_mut(&msg.controller_uid) { - controller.apply_relay_states(&msg.relay_states); - } - self.notify_relay_clients(); - } -} - -impl Handler for AppState { - type Result = Result, EmgauwaError>; - - fn handle(&mut self, _msg: GetRelays, _ctx: &mut Self::Context) -> Self::Result { - self.get_relays() - } -} - -impl Handler for AppState { - type Result = Result<(), EmgauwaError>; - - fn handle(&mut self, msg: Action, _ctx: &mut Self::Context) -> Self::Result { - log::debug!("Forwarding action: {:?}", msg.action); - if let Some((_, address)) = self.connected_controllers.get(&msg.controller_uid) { - // TODO: why does the block_on(send()) version not return? The AppState will be stuck. - //block_on(address.send(msg.action))? - address.do_send(msg.action); - Ok(()) - } else { - Err(EmgauwaError::Connection(msg.controller_uid)) - } - } -} - -impl Handler for AppState { - type Result = (); - - fn handle(&mut self, msg: ConnectRelayClient, _ctx: &mut Self::Context) -> Self::Result { - self.connected_relay_clients.push(msg.addr); - } -} diff --git a/emgauwa-core/src/handlers/mod.rs b/emgauwa-core/src/handlers/mod.rs deleted file mode 100644 index 3c13f6c..0000000 --- a/emgauwa-core/src/handlers/mod.rs +++ /dev/null @@ -1,37 +0,0 @@ -use actix_web::{error, Error, HttpRequest, HttpResponse}; -use serde::ser::SerializeStruct; -use serde::{Serialize, Serializer}; - -pub mod v1; - -enum EmgauwaJsonPayLoadError { - Error(error::JsonPayloadError), -} - -impl Serialize for EmgauwaJsonPayLoadError { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut s = serializer.serialize_struct("error", 3)?; - s.serialize_field("type", "json-payload-error")?; - s.serialize_field("code", &400)?; - s.serialize_field( - "description", - &match self { - EmgauwaJsonPayLoadError::Error(err) => format!("{}", err), - }, - )?; - s.end() - } -} - -pub fn json_error_handler(err: error::JsonPayloadError, _: &HttpRequest) -> Error { - error::InternalError::from_response( - "", - HttpResponse::BadRequest() - .content_type("application/json") - .json(EmgauwaJsonPayLoadError::Error(err)), - ) - .into() -} diff --git a/emgauwa-core/src/handlers/v1/controllers.rs b/emgauwa-core/src/handlers/v1/controllers.rs deleted file mode 100644 index 3fdb931..0000000 --- a/emgauwa-core/src/handlers/v1/controllers.rs +++ /dev/null @@ -1,92 +0,0 @@ -use actix::Addr; -use actix_web::{delete, get, put, web, HttpResponse}; -use emgauwa_common::db::DbController; -use emgauwa_common::errors::{DatabaseError, EmgauwaError}; -use emgauwa_common::models::{convert_db_list, Controller, FromDbModel}; -use emgauwa_common::types::{ControllerWsAction, EmgauwaUid, RequestControllerUpdate}; -use sqlx::{Pool, Sqlite}; - -use crate::app_state; -use crate::app_state::AppState; - -#[get("/controllers")] -pub async fn index(pool: web::Data>) -> Result { - let mut pool_conn = pool.acquire().await?; - - let db_controllers = DbController::get_all(&mut pool_conn).await?; - - let controllers: Vec = convert_db_list(&mut pool_conn, db_controllers)?; - - Ok(HttpResponse::Ok().json(controllers)) -} - -#[get("/controllers/{controller_id}")] -pub async fn show( - pool: web::Data>, - path: web::Path<(String,)>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (controller_uid,) = path.into_inner(); - let uid = EmgauwaUid::try_from(controller_uid.as_str())?; - - let controller = DbController::get_by_uid(&mut pool_conn, &uid) - .await? - .ok_or(DatabaseError::NotFound)?; - - let return_controller = Controller::from_db_model(&mut pool_conn, controller)?; - Ok(HttpResponse::Ok().json(return_controller)) -} - -#[put("/controllers/{controller_id}")] -pub async fn update( - pool: web::Data>, - app_state: web::Data>, - path: web::Path<(String,)>, - data: web::Json, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (controller_uid,) = path.into_inner(); - let uid = EmgauwaUid::try_from(controller_uid.as_str())?; - - let controller = DbController::get_by_uid(&mut pool_conn, &uid) - .await? - .ok_or(DatabaseError::NotFound)?; - - let controller = controller - .update(&mut pool_conn, data.name.as_str(), controller.relay_count) - .await?; - - let return_controller = Controller::from_db_model(&mut pool_conn, controller)?; - - app_state - .send(app_state::Action { - controller_uid: uid.clone(), - action: ControllerWsAction::Controller(return_controller.clone()), - }) - .await??; - - Ok(HttpResponse::Ok().json(return_controller)) -} - -#[delete("/controllers/{controller_id}")] -pub async fn delete( - pool: web::Data>, - app_state: web::Data>, - path: web::Path<(String,)>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (controller_uid,) = path.into_inner(); - let uid = EmgauwaUid::try_from(controller_uid.as_str())?; - - app_state - .send(app_state::DisconnectController { - controller_uid: uid.clone(), - }) - .await??; - - DbController::delete_by_uid(&mut pool_conn, uid).await?; - Ok(HttpResponse::Ok().json("controller got deleted")) -} diff --git a/emgauwa-core/src/handlers/v1/macros.rs b/emgauwa-core/src/handlers/v1/macros.rs deleted file mode 100644 index 0e3fc60..0000000 --- a/emgauwa-core/src/handlers/v1/macros.rs +++ /dev/null @@ -1,161 +0,0 @@ -use actix::Addr; -use actix_web::{delete, get, post, put, web, HttpResponse}; -use emgauwa_common::db::DbMacro; -use emgauwa_common::errors::{DatabaseError, EmgauwaError}; -use emgauwa_common::models::{convert_db_list, FromDbModel, Macro, MacroAction, Relay}; -use emgauwa_common::types::{ - ControllerWsAction, EmgauwaUid, RequestMacroCreate, RequestMacroExecute, RequestMacroUpdate, -}; -use itertools::Itertools; -use sqlx::{Pool, Sqlite}; - -use crate::app_state; -use crate::app_state::AppState; - -#[get("/macros")] -pub async fn index(pool: web::Data>) -> Result { - let mut pool_conn = pool.acquire().await?; - - let db_macros = DbMacro::get_all(&mut pool_conn).await?; - let macros: Vec = convert_db_list(&mut pool_conn, db_macros)?; - - Ok(HttpResponse::Ok().json(macros)) -} - -#[get("/macros/{macro_id}")] -pub async fn show( - pool: web::Data>, - path: web::Path<(String,)>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (macro_uid,) = path.into_inner(); - let uid = EmgauwaUid::try_from(macro_uid.as_str())?; - - let db_macro = DbMacro::get_by_uid(&mut pool_conn, &uid) - .await? - .ok_or(DatabaseError::NotFound)?; - - let return_macro = Macro::from_db_model(&mut pool_conn, db_macro)?; - Ok(HttpResponse::Ok().json(return_macro)) -} - -#[post("/macros")] -pub async fn add( - pool: web::Data>, - data: web::Json, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let new_macro = DbMacro::create(&mut pool_conn, EmgauwaUid::default(), &data.name).await?; - - new_macro - .set_actions(&mut pool_conn, data.actions.as_slice()) - .await?; - - let return_macro = Macro::from_db_model(&mut pool_conn, new_macro)?; - Ok(HttpResponse::Created().json(return_macro)) -} - -#[put("/macros/{macro_id}")] -pub async fn update( - pool: web::Data>, - path: web::Path<(String,)>, - data: web::Json, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (macro_uid,) = path.into_inner(); - let uid = EmgauwaUid::try_from(macro_uid.as_str())?; - - let db_macro = DbMacro::get_by_uid(&mut pool_conn, &uid) - .await? - .ok_or(DatabaseError::NotFound)?; - - if let Some(name) = &data.name { - db_macro.update(&mut pool_conn, name).await?; - } - - if let Some(actions) = &data.actions { - db_macro - .set_actions(&mut pool_conn, actions.as_slice()) - .await?; - } - - let return_macro = Macro::from_db_model(&mut pool_conn, db_macro)?; - Ok(HttpResponse::Ok().json(return_macro)) -} - -#[delete("/macros/{macro_id}")] -pub async fn delete( - pool: web::Data>, - path: web::Path<(String,)>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (macro_uid,) = path.into_inner(); - let uid = EmgauwaUid::try_from(macro_uid.as_str())?; - - DbMacro::delete_by_uid(&mut pool_conn, uid).await?; - Ok(HttpResponse::Ok().json("macro got deleted")) -} - -#[put("/macros/{macro_id}/execute")] -pub async fn execute( - pool: web::Data>, - app_state: web::Data>, - path: web::Path<(String,)>, - query: web::Query, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (macro_uid,) = path.into_inner(); - let uid = EmgauwaUid::try_from(macro_uid.as_str())?; - - let db_macro = DbMacro::get_by_uid(&mut pool_conn, &uid) - .await? - .ok_or(DatabaseError::NotFound)?; - - let actions_db = match query.weekday { - None => db_macro.get_actions(&mut pool_conn).await?, - Some(weekday) => { - db_macro - .get_actions_weekday(&mut pool_conn, weekday) - .await? - } - }; - let mut actions: Vec = convert_db_list(&mut pool_conn, actions_db)?; - - for action in &actions { - action.execute(&mut pool_conn).await?; - } - - let affected_controller_uids: Vec = actions - .iter() - .map(|action| action.relay.controller_id.clone()) - .unique() - .collect(); - - for controller_uid in affected_controller_uids { - let mut affected_relays: Vec = Vec::new(); - let mut affected_relay_ids: Vec = Vec::new(); - - for action in actions.iter_mut() { - if affected_relay_ids.contains(&action.relay.r.id) { - continue; - } - action.relay.reload(&mut pool_conn)?; - affected_relays.push(action.relay.clone()); - affected_relay_ids.push(action.relay.r.id); - } - - app_state - .send(app_state::Action { - controller_uid, - action: ControllerWsAction::Relays(affected_relays.clone()), - }) - .await??; - } - - Ok(HttpResponse::Ok().finish()) // TODO add a message? -} diff --git a/emgauwa-core/src/handlers/v1/mod.rs b/emgauwa-core/src/handlers/v1/mod.rs deleted file mode 100644 index 9ce232c..0000000 --- a/emgauwa-core/src/handlers/v1/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod controllers; -pub mod macros; -pub mod relays; -pub mod schedules; -pub mod tags; -pub mod ws; diff --git a/emgauwa-core/src/handlers/v1/relays.rs b/emgauwa-core/src/handlers/v1/relays.rs deleted file mode 100644 index f07c081..0000000 --- a/emgauwa-core/src/handlers/v1/relays.rs +++ /dev/null @@ -1,185 +0,0 @@ -use actix::Addr; -use actix_web::{get, post, put, web, HttpResponse}; -use emgauwa_common::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbTag}; -use emgauwa_common::errors::{DatabaseError, EmgauwaError}; -use emgauwa_common::models::{convert_db_list, FromDbModel, Relay}; -use emgauwa_common::types::{ - ControllerWsAction, EmgauwaUid, RequestRelayPulse, RequestRelayUpdate, -}; -use emgauwa_common::utils; -use sqlx::{Pool, Sqlite}; - -use crate::app_state; -use crate::app_state::AppState; - -#[get("/relays")] -pub async fn index(pool: web::Data>) -> Result { - let mut pool_conn = pool.acquire().await?; - - let db_relays = DbRelay::get_all(&mut pool_conn).await?; - - let relays: Vec = convert_db_list(&mut pool_conn, db_relays)?; - - Ok(HttpResponse::Ok().json(relays)) -} - -#[get("/relays/tag/{tag}")] -pub async fn tagged( - pool: web::Data>, - path: web::Path<(String,)>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (tag,) = path.into_inner(); - let tag_db = DbTag::get_by_tag(&mut pool_conn, &tag) - .await? - .ok_or(DatabaseError::NotFound)?; - - let db_relays = DbRelay::get_by_tag(&mut pool_conn, &tag_db).await?; - let relays: Vec = convert_db_list(&mut pool_conn, db_relays)?; - - Ok(HttpResponse::Ok().json(relays)) -} - -#[get("/controllers/{controller_id}/relays")] -pub async fn index_for_controller( - pool: web::Data>, - path: web::Path<(String,)>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (controller_uid,) = path.into_inner(); - let uid = EmgauwaUid::try_from(controller_uid.as_str())?; - - let controller = DbController::get_by_uid(&mut pool_conn, &uid) - .await? - .ok_or(DatabaseError::NotFound)?; - - let db_relays = controller.get_relays(&mut pool_conn).await?; - - let relays: Vec = convert_db_list(&mut pool_conn, db_relays)?; - Ok(HttpResponse::Ok().json(relays)) -} - -#[get("/controllers/{controller_id}/relays/{relay_num}")] -pub async fn show_for_controller( - pool: web::Data>, - path: web::Path<(String, i64)>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (controller_uid, relay_num) = path.into_inner(); - let uid = EmgauwaUid::try_from(controller_uid.as_str())?; - - let controller = DbController::get_by_uid(&mut pool_conn, &uid) - .await? - .ok_or(DatabaseError::NotFound)?; - - let relay = DbRelay::get_by_controller_and_num(&mut pool_conn, &controller, relay_num) - .await? - .ok_or(DatabaseError::NotFound)?; - - let return_relay = Relay::from_db_model(&mut pool_conn, relay)?; - Ok(HttpResponse::Ok().json(return_relay)) -} - -#[put("/controllers/{controller_id}/relays/{relay_num}")] -pub async fn update_for_controller( - pool: web::Data>, - app_state: web::Data>, - path: web::Path<(String, i64)>, - data: web::Json, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (controller_uid, relay_num) = path.into_inner(); - let uid = EmgauwaUid::try_from(controller_uid.as_str())?; - - let controller = DbController::get_by_uid(&mut pool_conn, &uid) - .await? - .ok_or(DatabaseError::NotFound)?; - - let mut relay = DbRelay::get_by_controller_and_num(&mut pool_conn, &controller, relay_num) - .await? - .ok_or(DatabaseError::NotFound)?; - - if let Some(name) = &data.name { - relay = relay.update(&mut pool_conn, name.as_str()).await?; - } - - if let Some(schedule_uids) = &data.schedules { - if schedule_uids.len() == 7 { - let mut schedules = Vec::new(); - for s_uid in schedule_uids { - schedules.push(s_uid.get_schedule(&mut pool_conn).await?); - } - - DbJunctionRelaySchedule::set_schedules( - &mut pool_conn, - &relay, - schedules.iter().collect(), - ) - .await?; - } - } - - if let Some(s_uid) = &data.active_schedule { - let schedule = s_uid.get_schedule(&mut pool_conn).await?; - DbJunctionRelaySchedule::set_schedule( - &mut pool_conn, - &relay, - &schedule, - utils::get_weekday(), - ) - .await?; - } - - if let Some(tags) = &data.tags { - relay.set_tags(&mut pool_conn, tags.as_slice()).await?; - } - - let relay = relay.reload(&mut pool_conn).await?; - - let return_relay = Relay::from_db_model(&mut pool_conn, relay)?; - - app_state - .send(app_state::Action { - controller_uid: uid, - action: ControllerWsAction::Relays(vec![return_relay.clone()]), - }) - .await??; - - Ok(HttpResponse::Ok().json(return_relay)) -} - -#[post("/controllers/{controller_id}/relays/{relay_num}/pulse")] -pub async fn pulse( - pool: web::Data>, - app_state: web::Data>, - path: web::Path<(String, i64)>, - data: web::Json, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (controller_uid, relay_num) = path.into_inner(); - let uid = EmgauwaUid::try_from(controller_uid.as_str())?; - - let controller = DbController::get_by_uid(&mut pool_conn, &uid) - .await? - .ok_or(DatabaseError::NotFound)?; - - let relay = DbRelay::get_by_controller_and_num(&mut pool_conn, &controller, relay_num) - .await? - .ok_or(DatabaseError::NotFound)?; - - let duration = data.duration.filter(|&d| d > 0); - - app_state - .send(app_state::Action { - controller_uid: uid, - action: ControllerWsAction::RelayPulse((relay.number, duration)), - }) - .await??; - - Ok(HttpResponse::Ok().finish()) // TODO add a message? -} diff --git a/emgauwa-core/src/handlers/v1/schedules.rs b/emgauwa-core/src/handlers/v1/schedules.rs deleted file mode 100644 index 7c8ccc6..0000000 --- a/emgauwa-core/src/handlers/v1/schedules.rs +++ /dev/null @@ -1,197 +0,0 @@ -use actix::Addr; -use actix_web::{delete, get, post, put, web, HttpResponse}; -use emgauwa_common::db::{DbController, DbJunctionRelaySchedule, DbSchedule, DbTag}; -use emgauwa_common::errors::{ApiError, DatabaseError, EmgauwaError}; -use emgauwa_common::models::{convert_db_list, FromDbModel, Schedule}; -use emgauwa_common::types::{ - ControllerWsAction, RequestScheduleCreate, RequestScheduleUpdate, ScheduleUid, -}; -use itertools::Itertools; -use sqlx::pool::PoolConnection; -use sqlx::{Pool, Sqlite}; - -use crate::app_state; -use crate::app_state::AppState; - -#[get("/schedules")] -pub async fn index(pool: web::Data>) -> Result { - let mut pool_conn = pool.acquire().await?; - - let db_schedules = DbSchedule::get_all(&mut pool_conn).await?; - let schedules: Vec = convert_db_list(&mut pool_conn, db_schedules)?; - - Ok(HttpResponse::Ok().json(schedules)) -} - -#[get("/schedules/tag/{tag}")] -pub async fn tagged( - pool: web::Data>, - path: web::Path<(String,)>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (tag,) = path.into_inner(); - let tag_db = DbTag::get_by_tag(&mut pool_conn, &tag) - .await? - .ok_or(DatabaseError::NotFound)?; - - let db_schedules = DbSchedule::get_by_tag(&mut pool_conn, &tag_db).await?; - let schedules: Vec = convert_db_list(&mut pool_conn, db_schedules)?; - - Ok(HttpResponse::Ok().json(schedules)) -} - -#[get("/schedules/{schedule_id}")] -pub async fn show( - pool: web::Data>, - path: web::Path<(String,)>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (schedule_uid,) = path.into_inner(); - let uid = ScheduleUid::try_from(schedule_uid.as_str())?; - - let schedule = DbSchedule::get_by_uid(&mut pool_conn, &uid) - .await? - .ok_or(DatabaseError::NotFound)?; - - let return_schedule = Schedule::from_db_model(&mut pool_conn, schedule)?; - Ok(HttpResponse::Ok().json(return_schedule)) -} - -#[post("/schedules")] -pub async fn add( - pool: web::Data>, - data: web::Json, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let new_schedule = DbSchedule::create( - &mut pool_conn, - ScheduleUid::default(), - &data.name, - &data.periods, - ) - .await?; - - if let Some(tags) = &data.tags { - new_schedule - .set_tags(&mut pool_conn, tags.as_slice()) - .await?; - } - - let return_schedule = Schedule::from_db_model(&mut pool_conn, new_schedule)?; - Ok(HttpResponse::Created().json(return_schedule)) -} - -async fn add_list_single( - conn: &mut PoolConnection, - request_schedule: &RequestScheduleCreate, -) -> Result { - let new_schedule = DbSchedule::create( - conn, - ScheduleUid::default(), - &request_schedule.name, - &request_schedule.periods, - ) - .await?; - - if let Some(tags) = &request_schedule.tags { - new_schedule.set_tags(conn, tags.as_slice()).await?; - } - - Ok(new_schedule) -} - -#[post("/schedules/list")] -pub async fn add_list( - pool: web::Data>, - data: web::Json>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let mut db_schedules: Vec = Vec::new(); - for s in data.iter() { - let new_s = futures::executor::block_on(add_list_single(&mut pool_conn, s))?; - db_schedules.push(new_s); - } - - let schedules: Vec = convert_db_list(&mut pool_conn, db_schedules)?; - Ok(HttpResponse::Created().json(schedules)) -} - -#[put("/schedules/{schedule_id}")] -pub async fn update( - pool: web::Data>, - app_state: web::Data>, - path: web::Path<(String,)>, - data: web::Json, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (schedule_uid,) = path.into_inner(); - let uid = ScheduleUid::try_from(schedule_uid.as_str())?; - - let schedule = DbSchedule::get_by_uid(&mut pool_conn, &uid) - .await? - .ok_or(DatabaseError::NotFound)?; - - let name = match &data.name { - None => schedule.name.as_str(), - Some(name) => name.as_str(), - }; - - let periods = match &data.periods { - None => &schedule.periods, - Some(period) => period, - }; - - let schedule = schedule.update(&mut pool_conn, name, periods).await?; - - if let Some(tags) = &data.tags { - schedule.set_tags(&mut pool_conn, tags.as_slice()).await?; - } - - let controller_ids: Vec = DbJunctionRelaySchedule::get_relays(&mut pool_conn, &schedule) - .await? - .into_iter() - .map(|r| r.controller_id) - .unique() - .collect(); - - for controller_id in controller_ids { - let controller = DbController::get(&mut pool_conn, controller_id) - .await? - .ok_or(DatabaseError::NotFound)?; - app_state - .send(app_state::Action { - controller_uid: controller.uid, - action: ControllerWsAction::Schedules(vec![schedule.clone()]), - }) - .await??; - } - - - let return_schedule = Schedule::from_db_model(&mut pool_conn, schedule)?; - Ok(HttpResponse::Ok().json(return_schedule)) -} - -#[delete("/schedules/{schedule_id}")] -pub async fn delete( - pool: web::Data>, - path: web::Path<(String,)>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (schedule_uid,) = path.into_inner(); - let uid = ScheduleUid::try_from(schedule_uid.as_str())?; - - match uid { - ScheduleUid::Off => Err(EmgauwaError::from(ApiError::ProtectedSchedule)), - ScheduleUid::On => Err(EmgauwaError::from(ApiError::ProtectedSchedule)), - ScheduleUid::Any(_) => { - DbSchedule::delete_by_uid(&mut pool_conn, uid).await?; - Ok(HttpResponse::Ok().json("schedule got deleted")) - } - } -} diff --git a/emgauwa-core/src/handlers/v1/tags.rs b/emgauwa-core/src/handlers/v1/tags.rs deleted file mode 100644 index cf4ef36..0000000 --- a/emgauwa-core/src/handlers/v1/tags.rs +++ /dev/null @@ -1,61 +0,0 @@ -use actix_web::{delete, get, post, web, HttpResponse}; -use emgauwa_common::db::DbTag; -use emgauwa_common::errors::{DatabaseError, EmgauwaError}; -use emgauwa_common::models::{FromDbModel, Tag}; -use emgauwa_common::types::RequestTagCreate; -use sqlx::{Pool, Sqlite}; - -#[get("/tags")] -pub async fn index(pool: web::Data>) -> Result { - let mut pool_conn = pool.acquire().await?; - - let db_tags = DbTag::get_all(&mut pool_conn).await?; - - let tags: Vec = db_tags.iter().map(|t| t.tag.clone()).collect(); - - Ok(HttpResponse::Ok().json(tags)) -} - -#[get("/tags/{tag_name}")] -pub async fn show( - pool: web::Data>, - path: web::Path<(String,)>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (tag_name,) = path.into_inner(); - - let tag = DbTag::get_by_tag(&mut pool_conn, &tag_name) - .await? - .ok_or(DatabaseError::NotFound)?; - - let return_tag = Tag::from_db_model(&mut pool_conn, tag)?; - Ok(HttpResponse::Ok().json(return_tag)) -} - -#[delete("/tags/{tag_name}")] -pub async fn delete( - pool: web::Data>, - path: web::Path<(String,)>, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let (tag_name,) = path.into_inner(); - - DbTag::delete_by_tag(&mut pool_conn, &tag_name).await?; - Ok(HttpResponse::Ok().json("tag got deleted")) -} - -#[post("/tags")] -pub async fn add( - pool: web::Data>, - data: web::Json, -) -> Result { - let mut pool_conn = pool.acquire().await?; - - let new_tag = DbTag::create(&mut pool_conn, &data.tag).await?; - - let cache = (Vec::new(), Vec::new()); // a new tag can't have any relays or schedules - let return_tag = Tag::from_db_model_cache(&mut pool_conn, new_tag, cache)?; - Ok(HttpResponse::Created().json(return_tag)) -} diff --git a/emgauwa-core/src/handlers/v1/ws/controllers/handlers.rs b/emgauwa-core/src/handlers/v1/ws/controllers/handlers.rs deleted file mode 100644 index 9a01a7b..0000000 --- a/emgauwa-core/src/handlers/v1/ws/controllers/handlers.rs +++ /dev/null @@ -1,114 +0,0 @@ -use actix::{Actor, AsyncContext}; -use emgauwa_common::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule}; -use emgauwa_common::errors::{DatabaseError, EmgauwaError}; -use emgauwa_common::models::{Controller, FromDbModel}; -use emgauwa_common::types::{ControllerWsAction, EmgauwaUid, RelayStates}; -use emgauwa_common::utils; -use futures::executor::block_on; -use sqlx::pool::PoolConnection; -use sqlx::Sqlite; - -use crate::app_state::{Action, ConnectController, UpdateRelayStates}; -use crate::handlers::v1::ws::controllers::ControllersWs; - -impl ControllersWs { - pub fn handle_register( - &mut self, - conn: &mut PoolConnection, - ctx: &mut ::Context, - controller: Controller, - ) -> Result<(), EmgauwaError> { - log::info!( - "Registering controller: {} ({})", - controller.c.name, - controller.c.uid - ); - let c = &controller.c; - let controller_db = block_on(DbController::get_by_uid_or_create( - conn, - &c.uid, - &c.name, - c.relay_count, - ))?; - block_on(controller_db.update_active(conn, true))?; - // update only the relay count - block_on(controller_db.update(conn, &controller_db.name, c.relay_count))?; - - for relay in &controller.relays { - log::debug!( - "Registering relay: {} ({})", - relay.r.name, - match relay.is_on { - Some(true) => "+", - Some(false) => "-", - None => "?", - } - ); - let (new_relay, created) = block_on(DbRelay::get_by_controller_and_num_or_create( - conn, - &controller_db, - relay.r.number, - &relay.r.name, - ))?; - if created { - let mut relay_schedules = Vec::new(); - for schedule in &relay.schedules { - let (new_schedule, _) = block_on(DbSchedule::get_by_uid_or_create( - conn, - schedule.uid.clone(), - &schedule.name, - &schedule.periods, - ))?; - relay_schedules.push(new_schedule); - } - - block_on(DbJunctionRelaySchedule::set_schedules( - conn, - &new_relay, - relay_schedules.iter().collect(), - ))?; - } - } - - let controller_uid = &controller.c.uid; - let controller_db = block_on(DbController::get_by_uid(conn, controller_uid))? - .ok_or(DatabaseError::InsertGetError)?; - let controller = Controller::from_db_model(conn, controller_db)?; - - let addr = ctx.address(); - self.controller_uid = Some(controller_uid.clone()); - block_on(self.app_state.send(ConnectController { - address: addr.recipient(), - controller: controller.clone(), - }))??; - - block_on(self.app_state.send(Action { - controller_uid: controller_uid.clone(), - action: ControllerWsAction::Controller(controller.clone()), - }))??; - block_on(self.app_state.send(Action { - controller_uid: controller_uid.clone(), - action: ControllerWsAction::Relays(controller.relays), - }))??; - - log::debug!("Done registering controller"); - Ok(()) - } - - pub fn handle_relay_states( - &mut self, - controller_uid: EmgauwaUid, - relay_states: RelayStates, - ) -> Result<(), EmgauwaError> { - log::debug!( - "Received relay states: {} for {}", - utils::printable_relay_states(&relay_states), - controller_uid - ); - block_on(self.app_state.send(UpdateRelayStates { - controller_uid, - relay_states, - }))?; - Ok(()) - } -} diff --git a/emgauwa-core/src/handlers/v1/ws/controllers/mod.rs b/emgauwa-core/src/handlers/v1/ws/controllers/mod.rs deleted file mode 100644 index 2b39e5a..0000000 --- a/emgauwa-core/src/handlers/v1/ws/controllers/mod.rs +++ /dev/null @@ -1,154 +0,0 @@ -mod handlers; - -use std::time::Instant; - -use actix::{Actor, ActorContext, Addr, AsyncContext, Handler, StreamHandler}; -use actix_web_actors::ws; -use actix_web_actors::ws::ProtocolError; -use emgauwa_common::constants::{HEARTBEAT_INTERVAL, HEARTBEAT_TIMEOUT}; -use emgauwa_common::errors::EmgauwaError; -use emgauwa_common::types::{ControllerWsAction, EmgauwaUid}; -use futures::executor::block_on; -use sqlx::pool::PoolConnection; -use sqlx::{Pool, Sqlite}; -use ws::Message; - -use crate::app_state::{AppState, DisconnectController}; -use crate::utils::flatten_result; - -pub struct ControllersWs { - pub pool: Pool, - pub controller_uid: Option, - pub app_state: Addr, - pub hb: Instant, -} - -impl Actor for ControllersWs { - type Context = ws::WebsocketContext; - - fn started(&mut self, ctx: &mut Self::Context) { - self.hb(ctx); - } - - fn stopped(&mut self, _ctx: &mut Self::Context) { - if let Some(controller_uid) = &self.controller_uid { - let flat_res = flatten_result( - block_on(self.app_state.send(DisconnectController { - controller_uid: controller_uid.clone(), - })) - .map_err(EmgauwaError::from), - ); - if let Err(err) = flat_res { - log::error!("Error disconnecting controller: {:?}", err); - } - } - } -} - -impl ControllersWs { - pub fn handle_action( - &mut self, - conn: &mut PoolConnection, - ctx: &mut ::Context, - action: ControllerWsAction, - ) { - let action_res = match action { - ControllerWsAction::Register(controller) => self.handle_register(conn, ctx, controller), - ControllerWsAction::RelayStates((controller_uid, relay_states)) => { - self.handle_relay_states(controller_uid, relay_states) - } - _ => Ok(()), - }; - if let Err(e) = action_res { - log::error!("Error handling action: {:?}", e); - ctx.text( - serde_json::to_string(&e).unwrap_or(format!("Error in handling action: {:?}", e)), - ); - } - } - - // helper method that sends ping to client every 5 seconds (HEARTBEAT_INTERVAL). - fn hb(&self, ctx: &mut ws::WebsocketContext) { - ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { - // check client heartbeats - if Instant::now().duration_since(act.hb) > HEARTBEAT_TIMEOUT { - log::warn!("Websocket Controller heartbeat failed, disconnecting!"); - ctx.stop(); - // don't try to send a ping - return; - } - - ctx.ping(&[]); - }); - } -} - -impl Handler for ControllersWs { - type Result = Result<(), EmgauwaError>; - - fn handle(&mut self, action: ControllerWsAction, ctx: &mut Self::Context) -> Self::Result { - match action { - ControllerWsAction::Disconnect => { - ctx.close(None); - ctx.stop(); - } - _ => { - let action_json = serde_json::to_string(&action)?; - ctx.text(action_json); - } - } - Ok(()) - } -} - -impl StreamHandler> for ControllersWs { - fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { - let mut pool_conn = match block_on(self.pool.acquire()) { - Ok(conn) => conn, - Err(err) => { - log::error!("Failed to acquire database connection: {:?}", err); - ctx.stop(); - return; - } - }; - - let msg = match msg { - Err(_) => { - ctx.stop(); - return; - } - Ok(msg) => msg, - }; - - match msg { - Message::Ping(msg) => { - self.hb = Instant::now(); - ctx.pong(&msg) - } - Message::Pong(_) => { - self.hb = Instant::now(); - } - Message::Text(text) => match serde_json::from_str(&text) { - Ok(action) => { - self.handle_action(&mut pool_conn, ctx, action); - } - Err(e) => { - log::error!("Error deserializing action: {:?}", e); - ctx.text( - serde_json::to_string(&EmgauwaError::Serialization(e)) - .unwrap_or(String::from("Error in deserializing action")), - ); - } - }, - Message::Binary(_) => log::warn!("Received unexpected binary in controller ws"), - Message::Close(reason) => { - ctx.close(reason); - ctx.stop(); - } - Message::Continuation(_) => { - ctx.stop(); - } - Message::Nop => (), - } - } -} diff --git a/emgauwa-core/src/handlers/v1/ws/mod.rs b/emgauwa-core/src/handlers/v1/ws/mod.rs deleted file mode 100644 index 8507a67..0000000 --- a/emgauwa-core/src/handlers/v1/ws/mod.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::time::Instant; - -use actix::Addr; -use actix_web::{get, web, HttpRequest, HttpResponse}; -use actix_web_actors::ws; -use emgauwa_common::errors::EmgauwaError; -use sqlx::{Pool, Sqlite}; - -use crate::app_state::AppState; -use crate::handlers::v1::ws::controllers::ControllersWs; -use crate::handlers::v1::ws::relays::RelaysWs; - -pub mod controllers; -pub mod relays; - -#[get("/ws/controllers")] -pub async fn ws_controllers( - pool: web::Data>, - app_state: web::Data>, - req: HttpRequest, - stream: web::Payload, -) -> Result { - let resp = ws::start( - ControllersWs { - pool: pool.get_ref().clone(), - controller_uid: None, - app_state: app_state.get_ref().clone(), - hb: Instant::now(), - }, - &req, - stream, - ) - .map_err(|_| EmgauwaError::Internal(String::from("error starting websocket"))); - resp -} - -#[get("/ws/relays")] -pub async fn ws_relays( - app_state: web::Data>, - req: HttpRequest, - stream: web::Payload, -) -> Result { - let resp = ws::start( - RelaysWs { - app_state: app_state.get_ref().clone(), - hb: Instant::now(), - }, - &req, - stream, - ) - .map_err(|_| EmgauwaError::Internal(String::from("error starting websocket"))); - resp -} diff --git a/emgauwa-core/src/handlers/v1/ws/relays/mod.rs b/emgauwa-core/src/handlers/v1/ws/relays/mod.rs deleted file mode 100644 index f4dd9e2..0000000 --- a/emgauwa-core/src/handlers/v1/ws/relays/mod.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::time::Instant; - -use actix::{Actor, ActorContext, Addr, AsyncContext, Handler, Message, StreamHandler}; -use actix_web_actors::ws; -use actix_web_actors::ws::ProtocolError; -use emgauwa_common::constants::{HEARTBEAT_INTERVAL, HEARTBEAT_TIMEOUT}; -use emgauwa_common::errors::EmgauwaError; -use futures::executor::block_on; - -use crate::app_state::{AppState, ConnectRelayClient}; - -pub struct RelaysWs { - pub app_state: Addr, - pub hb: Instant, -} - -#[derive(Message)] -#[rtype(result = "()")] -pub struct SendRelays { - pub relays_json: String, -} - -impl Actor for RelaysWs { - type Context = ws::WebsocketContext; - - fn started(&mut self, ctx: &mut Self::Context) { - // get unique id for ctx - match self.get_relays_json() { - Ok(relays_json) => { - ctx.text(relays_json); - self.hb(ctx); - - block_on(self.app_state.send(ConnectRelayClient { - addr: ctx.address(), - })) - .unwrap(); - } - Err(err) => { - log::error!("Error getting relays: {:?}", err); - ctx.stop(); - } - } - } -} - -impl RelaysWs { - fn get_relays_json(&self) -> Result { - let relays = block_on(self.app_state.send(crate::app_state::GetRelays {}))??; - serde_json::to_string(&relays).map_err(EmgauwaError::from) - } - - // helper method that sends ping to client every 5 seconds (HEARTBEAT_INTERVAL). - fn hb(&self, ctx: &mut ws::WebsocketContext) { - ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { - // check client heartbeats - if Instant::now().duration_since(act.hb) > HEARTBEAT_TIMEOUT { - log::debug!("Websocket Relay heartbeat failed, disconnecting!"); - ctx.stop(); - // don't try to send a ping - return; - } - - ctx.ping(&[]); - }); - } -} - -impl StreamHandler> for RelaysWs { - fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { - let msg = match msg { - Err(_) => { - ctx.stop(); - return; - } - Ok(msg) => msg, - }; - - match msg { - ws::Message::Ping(msg) => { - self.hb = Instant::now(); - ctx.pong(&msg) - } - ws::Message::Pong(_) => { - self.hb = Instant::now(); - } - ws::Message::Text(_) => log::debug!("Received unexpected text in relays ws"), - ws::Message::Binary(_) => log::debug!("Received unexpected binary in relays ws"), - ws::Message::Close(reason) => { - ctx.close(reason); - ctx.stop(); - } - ws::Message::Continuation(_) => { - ctx.stop(); - } - ws::Message::Nop => (), - } - } -} - -impl Handler for RelaysWs { - type Result = (); - - fn handle(&mut self, msg: SendRelays, ctx: &mut Self::Context) -> Self::Result { - ctx.text(msg.relays_json); - } -} diff --git a/emgauwa-core/src/main.rs b/emgauwa-core/src/main.rs deleted file mode 100644 index adbd86f..0000000 --- a/emgauwa-core/src/main.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::net::TcpListener; - -use actix::{Actor, Arbiter}; -use actix_cors::Cors; -use actix_web::middleware::TrailingSlash; -use actix_web::{middleware, web, App, HttpServer}; -use emgauwa_common::db::DbController; -use emgauwa_common::errors::EmgauwaError; -use emgauwa_common::utils::{drop_privileges, init_logging}; -use serde_json::json; -use utoipa_swagger_ui::SwaggerUi; - -use crate::app_state::AppState; - -mod app_state; -mod handlers; -mod settings; -mod utils; - -#[actix_web::main] -async fn main() -> Result<(), std::io::Error> { - let settings = settings::init()?; - - let listener = TcpListener::bind(format!("{}:{}", settings.server.host, settings.server.port))?; - drop_privileges(&settings.permissions)?; - - init_logging(&settings.logging.level)?; - - let pool = emgauwa_common::db::init(&settings.database).await?; - - let mut conn = pool.acquire().await.map_err(EmgauwaError::from)?; - DbController::all_inactive(&mut conn) - .await - .map_err(EmgauwaError::from)?; - conn.close().await.map_err(EmgauwaError::from)?; - - let app_state_arbiter = Arbiter::with_tokio_rt(|| { - tokio::runtime::Builder::new_multi_thread() - .worker_threads(2) - .enable_all() - .build() - .unwrap() - }); - let app_state_pool = pool.clone(); - let app_state = Actor::start_in_arbiter(&app_state_arbiter.handle(), move |_| { - AppState::new(app_state_pool) - }); - - log::info!( - "Starting server on {}:{}", - settings.server.host, - settings.server.port - ); - - HttpServer::new(move || { - let cors = Cors::default().allow_any_method().allow_any_header(); - - let origins = settings.origins.clone(); - let cors = match settings.origins.is_empty() { - true => cors.allow_any_origin(), - false => cors.allowed_origin_fn(move |origin, _req_head| { - origins.contains(&origin.to_str().unwrap_or_default().to_string()) - }), - }; - - let api_default = json!({ - "openapi": "3.0.0", - "info": { - "version": "0.0.0", - "title": "Failed to load API documentation", - } - }); - let api_v1_json = - serde_json::from_str(include_str!(concat!(env!("OUT_DIR"), "/api.v1.json"))) - .unwrap_or(api_default.clone()); - - App::new() - .wrap(cors) - .wrap(middleware::Logger::default()) - .app_data(web::JsonConfig::default().error_handler(handlers::json_error_handler)) - .app_data(web::Data::new(pool.clone())) - .app_data(web::Data::new(app_state.clone())) - .service( - SwaggerUi::new("/api/docs/{_:.*}") - .external_urls_from_iter_unchecked([("/api/v1.json", api_v1_json)]), - ) - .service( - web::scope("/api/v1") - .wrap(middleware::NormalizePath::new(TrailingSlash::Trim)) - .service(handlers::v1::controllers::index) - .service(handlers::v1::controllers::show) - .service(handlers::v1::controllers::update) - .service(handlers::v1::controllers::delete) - .service(handlers::v1::relays::index) - .service(handlers::v1::relays::tagged) - .service(handlers::v1::relays::index_for_controller) - .service(handlers::v1::relays::show_for_controller) - .service(handlers::v1::relays::update_for_controller) - .service(handlers::v1::relays::pulse) - .service(handlers::v1::schedules::index) - .service(handlers::v1::schedules::tagged) - .service(handlers::v1::schedules::show) - .service(handlers::v1::schedules::add) - .service(handlers::v1::schedules::add_list) - .service(handlers::v1::schedules::update) - .service(handlers::v1::schedules::delete) - .service(handlers::v1::tags::index) - .service(handlers::v1::tags::show) - .service(handlers::v1::tags::delete) - .service(handlers::v1::tags::add) - .service(handlers::v1::macros::index) - .service(handlers::v1::macros::show) - .service(handlers::v1::macros::add) - .service(handlers::v1::macros::update) - .service(handlers::v1::macros::delete) - .service(handlers::v1::macros::execute) - .service(handlers::v1::ws::ws_controllers) - .service(handlers::v1::ws::ws_relays), - ) - }) - .listen(listener)? - .run() - .await -} diff --git a/emgauwa-core/src/settings.rs b/emgauwa-core/src/settings.rs deleted file mode 100644 index 620bf7a..0000000 --- a/emgauwa-core/src/settings.rs +++ /dev/null @@ -1,32 +0,0 @@ -use emgauwa_common::errors::EmgauwaError; -use emgauwa_common::settings; -use serde_derive::Deserialize; - -#[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 origins: Vec, -} - -impl Default for Settings { - fn default() -> Self { - Settings { - server: settings::Server::default(), - database: String::from("sqlite://emgauwa-core.sqlite"), - permissions: settings::Permissions::default(), - logging: settings::Logging::default(), - - origins: Vec::new(), - } - } -} - -pub fn init() -> Result { - settings::load("core", "CORE") -} diff --git a/emgauwa-core/src/utils.rs b/emgauwa-core/src/utils.rs deleted file mode 100644 index a35de53..0000000 --- a/emgauwa-core/src/utils.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub fn flatten_result(res: Result, E>) -> Result { - match res { - Ok(Ok(t)) => Ok(t), - Ok(Err(e)) => Err(e), - Err(e) => Err(e), - } -} 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-controller/src/app_state.rs b/src/app_state.rs similarity index 100% rename from emgauwa-controller/src/app_state.rs rename to src/app_state.rs diff --git a/emgauwa-controller/src/driver.rs b/src/driver.rs similarity index 100% rename from emgauwa-controller/src/driver.rs rename to src/driver.rs diff --git a/emgauwa-controller/src/main.rs b/src/main.rs similarity index 100% rename from emgauwa-controller/src/main.rs rename to src/main.rs diff --git a/emgauwa-controller/src/relay_loop.rs b/src/relay_loop.rs similarity index 100% rename from emgauwa-controller/src/relay_loop.rs rename to src/relay_loop.rs diff --git a/emgauwa-controller/src/settings.rs b/src/settings.rs similarity index 100% rename from emgauwa-controller/src/settings.rs rename to src/settings.rs diff --git a/emgauwa-controller/src/utils.rs b/src/utils.rs similarity index 100% rename from emgauwa-controller/src/utils.rs rename to src/utils.rs diff --git a/emgauwa-controller/src/ws/mod.rs b/src/ws/mod.rs similarity index 100% rename from emgauwa-controller/src/ws/mod.rs rename to src/ws/mod.rs