From ae4b4707d97ebcbb968542cb4c6aa9f5ec2da98a Mon Sep 17 00:00:00 2001 From: Brent Schroeter Date: Tue, 13 May 2025 00:02:33 -0700 Subject: [PATCH] renders a table --- Cargo.lock | 850 +++++++++++------- Cargo.toml | 10 +- README.md | 7 + catalogs/src/lib.rs | 6 - catalogs/src/pg_attribute.rs | 32 - catalogs/src/pg_class.rs | 25 - catalogs/src/pg_namespace.rs | 23 - diesel.toml | 2 +- {catalogs => mdengine}/Cargo.lock | 0 {catalogs => mdengine}/Cargo.toml | 2 +- {catalogs => mdengine}/Makefile | 0 mdengine/README.md | 7 + {catalogs => mdengine}/diesel.toml | 0 mdengine/src/lib.rs | 41 + mdengine/src/pg_attribute.rs | 75 ++ mdengine/src/pg_class.rs | 14 + mdengine/src/pg_namespace.rs | 13 + {catalogs => mdengine}/src/pg_roles.rs | 45 +- .../src/schema.rs | 14 +- .../src/table_privileges.rs | 35 +- src/abstract_.rs | 67 +- src/app_state.rs | 63 +- src/data_layer.rs | 118 +++ src/main.rs | 3 +- src/router.rs | 145 ++- src/users.rs | 2 +- templates/base.html | 1 - templates/tmp.html | 2 + 28 files changed, 1044 insertions(+), 558 deletions(-) create mode 100644 README.md delete mode 100644 catalogs/src/lib.rs delete mode 100644 catalogs/src/pg_attribute.rs delete mode 100644 catalogs/src/pg_class.rs delete mode 100644 catalogs/src/pg_namespace.rs rename {catalogs => mdengine}/Cargo.lock (100%) rename {catalogs => mdengine}/Cargo.toml (91%) rename {catalogs => mdengine}/Makefile (100%) create mode 100644 mdengine/README.md rename {catalogs => mdengine}/diesel.toml (100%) create mode 100644 mdengine/src/lib.rs create mode 100644 mdengine/src/pg_attribute.rs create mode 100644 mdengine/src/pg_class.rs create mode 100644 mdengine/src/pg_namespace.rs rename {catalogs => mdengine}/src/pg_roles.rs (61%) rename catalogs/src/catalogs_schema.rs => mdengine/src/schema.rs (97%) rename {catalogs => mdengine}/src/table_privileges.rs (58%) create mode 100644 src/data_layer.rs diff --git a/Cargo.lock b/Cargo.lock index d89b516..81f83c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,14 +19,14 @@ checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if 1.0.0", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -111,9 +111,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arraydeque" @@ -135,53 +135,51 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "askama" -version = "0.12.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" +checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" dependencies = [ "askama_derive", - "askama_escape", - "humansize", - "num-traits", + "itoa", "percent-encoding", + "serde", + "serde_json", ] [[package]] name = "askama_derive" -version = "0.12.5" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83" +checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" dependencies = [ "askama_parser", "basic-toml", - "mime", - "mime_guess", + "memchr", "proc-macro2", "quote", + "rustc-hash", "serde", + "serde_derive", "syn", ] -[[package]] -name = "askama_escape" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" - [[package]] name = "askama_parser" -version = "0.2.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0" +checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" dependencies = [ - "nom", + "memchr", + "serde", + "serde_derive", + "winnow", ] [[package]] name = "async-compression" -version = "0.4.20" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310c9bcae737a48ef5cdee3174184e6d548b292739ede61a1f955ef76a738861" +checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" dependencies = [ "flate2", "futures-core", @@ -212,9 +210,9 @@ dependencies = [ "bincode", "blake3", "chrono", - "hmac", + "hmac 0.11.0", "log", - "rand", + "rand 0.8.5", "serde", "serde_json", "sha2 0.9.9", @@ -222,9 +220,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.87" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", @@ -245,9 +243,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.8.1" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" +checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" dependencies = [ "axum-core", "axum-macros", @@ -280,12 +278,12 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" dependencies = [ "bytes", - "futures-util", + "futures-core", "http 1.3.1", "http-body 1.0.1", "http-body-util", @@ -300,9 +298,9 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b" +checksum = "45bf463831f5131b7d3c756525b305d40f1185b688565648a92e1392ca35713d" dependencies = [ "axum", "axum-core", @@ -316,6 +314,7 @@ dependencies = [ "http-body-util", "mime", "pin-project-lite", + "rustversion", "serde", "serde_html_form", "serde_path_to_error", @@ -337,9 +336,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if 1.0.0", @@ -452,19 +451,11 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" -[[package]] -name = "catalogs" -version = "0.1.0" -dependencies = [ - "chrono", - "diesel", -] - [[package]] name = "cc" -version = "1.2.16" +version = "1.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" dependencies = [ "shlex", ] @@ -498,9 +489,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.32" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -508,9 +499,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -576,7 +567,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "tiny-keccak", ] @@ -679,9 +670,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -689,9 +680,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", @@ -703,9 +694,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", @@ -735,6 +726,21 @@ dependencies = [ "diesel", ] +[[package]] +name = "deadpool-postgres" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d697d376cbfa018c23eb4caab1fd1883dd9c906a8c034e8d9a3cb06a7e0bef9" +dependencies = [ + "async-trait", + "deadpool", + "getrandom 0.2.16", + "serde", + "tokio", + "tokio-postgres", + "tracing", +] + [[package]] name = "deadpool-runtime" version = "0.1.4" @@ -755,9 +761,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -811,9 +817,9 @@ dependencies = [ [[package]] name = "diesel_derives" -version = "2.2.4" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93958254b70bea63b4187ff73d10180599d9d8d177071b7f91e6da4e0c0ad55" +checksum = "68d4216021b3ea446fd2047f5c8f8fe6e98af34508a254a01e4d6bc1e844f84d" dependencies = [ "diesel_table_macro_syntax", "dsl_auto_type", @@ -859,6 +865,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common", + "subtle", ] [[package]] @@ -924,9 +931,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -938,6 +945,12 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fastrand" version = "2.3.0" @@ -946,9 +959,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -1085,9 +1098,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -1098,14 +1111,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -1135,9 +1148,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" dependencies = [ "atomic-waker", "bytes", @@ -1164,9 +1177,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" [[package]] name = "hashlink" @@ -1223,6 +1236,15 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "http" version = "0.2.12" @@ -1297,15 +1319,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humansize" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" -dependencies = [ - "libm", -] - [[package]] name = "hyper" version = "0.14.32" @@ -1339,7 +1352,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.8", + "h2 0.4.10", "http 1.3.1", "http-body 1.0.1", "httparse", @@ -1375,7 +1388,7 @@ dependencies = [ "http 1.3.1", "hyper 1.6.0", "hyper-util", - "rustls 0.23.23", + "rustls 0.23.27", "rustls-pki-types", "tokio", "tokio-rustls 0.26.2", @@ -1400,9 +1413,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", @@ -1410,6 +1423,7 @@ dependencies = [ "http 1.3.1", "http-body 1.0.1", "hyper 1.6.0", + "libc", "pin-project-lite", "socket2", "tokio", @@ -1419,14 +1433,15 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -1442,21 +1457,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -1465,31 +1481,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -1497,67 +1493,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -1577,9 +1560,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -1587,12 +1570,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -1604,23 +1587,27 @@ dependencies = [ "async-session", "axum", "axum-extra", - "catalogs", "chrono", "clap", "config", "deadpool-diesel", + "deadpool-postgres", "derive_builder", "diesel", "diesel_migrations", "dotenvy", "futures", + "mdengine", + "native-tls", "oauth2", "percent-encoding", - "rand", - "reqwest 0.12.14", + "postgres-native-tls", + "rand 0.8.5", + "reqwest 0.12.15", "serde", "serde_json", "tokio", + "tokio-postgres", "tower", "tower-http", "tracing", @@ -1676,27 +1663,21 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" - -[[package]] -name = "libm" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" @@ -1710,9 +1691,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "matchers" @@ -1729,6 +1710,24 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if 1.0.0", + "digest 0.10.7", +] + +[[package]] +name = "mdengine" +version = "0.1.0" +dependencies = [ + "chrono", + "diesel", +] + [[package]] name = "memchr" version = "2.7.4" @@ -1780,9 +1779,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -1868,14 +1867,14 @@ checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" dependencies = [ "base64 0.13.1", "chrono", - "getrandom 0.2.15", + "getrandom 0.2.16", "http 0.2.12", - "rand", + "rand 0.8.5", "reqwest 0.11.27", "serde", "serde_json", "serde_path_to_error", - "sha2 0.10.8", + "sha2 0.10.9", "thiserror 1.0.69", "url", ] @@ -1891,9 +1890,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "opaque-debug" @@ -1903,9 +1902,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.71" +version = "0.10.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" dependencies = [ "bitflags 2.9.0", "cfg-if 1.0.0", @@ -1935,9 +1934,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.106" +version = "0.9.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" +checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" dependencies = [ "cc", "libc", @@ -1998,9 +1997,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ "memchr", "thiserror 2.0.12", @@ -2009,9 +2008,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" +checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" dependencies = [ "pest", "pest_generator", @@ -2019,9 +2018,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" +checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" dependencies = [ "pest", "pest_meta", @@ -2032,13 +2031,31 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" +checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" dependencies = [ "once_cell", "pest", - "sha2 0.10.8", + "sha2 0.10.9", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", ] [[package]] @@ -2059,6 +2076,58 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "postgres-native-tls" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f39498473c92f7b6820ae970382c1d83178a3454c618161cb772e8598d9f6f" +dependencies = [ + "native-tls", + "tokio", + "tokio-native-tls", + "tokio-postgres", +] + +[[package]] +name = "postgres-protocol" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" +dependencies = [ + "base64 0.22.1", + "byteorder", + "bytes", + "fallible-iterator", + "hmac 0.12.1", + "md-5", + "memchr", + "rand 0.9.1", + "sha2 0.10.9", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" +dependencies = [ + "bytes", + "chrono", + "fallible-iterator", + "postgres-protocol", + "uuid", +] + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2071,7 +2140,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.23", + "zerocopy", ] [[package]] @@ -2108,9 +2177,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -2124,6 +2193,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.8.5" @@ -2131,8 +2206,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -2142,7 +2227,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -2151,14 +2246,23 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", ] [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags 2.9.0", ] @@ -2250,16 +2354,16 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.14" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e327e510263980e231de548a33e63d34962d29ae61b467389a1a09627a254" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.4.8", + "h2 0.4.10", "http 1.3.1", "http-body 1.0.1", "http-body-util", @@ -2300,7 +2404,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if 1.0.0", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -2335,10 +2439,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] -name = "rustix" -version = "1.0.2" +name = "rustc-hash" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags 2.9.0", "errno", @@ -2361,13 +2471,13 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.23" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ "once_cell", "rustls-pki-types", - "rustls-webpki 0.102.8", + "rustls-webpki 0.103.3", "subtle", "zeroize", ] @@ -2392,9 +2502,12 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] [[package]] name = "rustls-webpki" @@ -2408,9 +2521,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.8" +version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ "ring", "rustls-pki-types", @@ -2579,9 +2692,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -2605,13 +2718,19 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -2623,15 +2742,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2643,6 +2762,17 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "strsim" version = "0.11.1" @@ -2657,9 +2787,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -2683,9 +2813,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -2736,12 +2866,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.19.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.1", + "getrandom 0.3.3", "once_cell", "rustix", "windows-sys 0.59.0", @@ -2799,9 +2929,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.39" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -2814,15 +2944,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -2839,19 +2969,34 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", ] [[package]] -name = "tokio" -version = "1.44.1" +name = "tinyvec" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -2886,6 +3031,32 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-postgres" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand 0.9.1", + "socket2", + "tokio", + "tokio-util", + "whoami", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -2902,15 +3073,15 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.23", + "rustls 0.23.27", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -2921,9 +3092,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -2933,26 +3104,33 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + [[package]] name = "tower" version = "0.5.2" @@ -2971,9 +3149,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" dependencies = [ "async-compression", "bitflags 2.9.0", @@ -3095,12 +3273,33 @@ version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + [[package]] name = "unicode-segmentation" version = "1.12.0" @@ -3125,12 +3324,6 @@ dependencies = [ "serde", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -3149,7 +3342,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.3", "serde", ] @@ -3218,13 +3411,19 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -3312,6 +3511,17 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "whoami" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +dependencies = [ + "redox_syscall", + "wasite", + "web-sys", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3336,18 +3546,44 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.52.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "windows-link" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] name = "windows-registry" @@ -3356,15 +3592,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", - "windows-strings", + "windows-strings 0.3.1", "windows-targets 0.53.0", ] [[package]] name = "windows-result" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ "windows-link", ] @@ -3378,6 +3614,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3592,9 +3837,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -3611,24 +3856,18 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags 2.9.0", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "yaml-rust2" @@ -3643,9 +3882,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -3655,9 +3894,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -3667,38 +3906,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" -dependencies = [ - "zerocopy-derive 0.8.23", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", @@ -3733,10 +3952,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] -name = "zerovec" -version = "0.10.4" +name = "zerotrie" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -3745,9 +3975,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 7f911be..d752913 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,26 +4,29 @@ version = "0.0.1" edition = "2021" [workspace] -members = ["catalogs"] +members = ["mdengine"] [dependencies] anyhow = "1.0.91" -askama = { version = "0.12.1", features = ["urlencode"] } +askama = { version = "0.14.0", features = ["urlencode"] } async-session = "3.0.0" axum = { version = "0.8.1", features = ["macros"] } axum-extra = { version = "0.10.0", features = ["cookie", "form", "typed-header"] } -catalogs = { path = "./catalogs" } +mdengine = { path = "./mdengine" } chrono = { version = "0.4.39", features = ["serde"] } clap = { version = "4.5.31", features = ["derive"] } config = "0.14.1" deadpool-diesel = { version = "0.6.1", features = ["postgres", "serde"] } +deadpool-postgres = { version = "0.14.1", features = ["serde"] } derive_builder = "0.20.2" diesel = { version = "2.2.10", features = ["postgres", "chrono", "uuid", "serde_json"] } diesel_migrations = { version = "2.2.0", features = ["postgres"] } dotenvy = "0.15.7" futures = "0.3.31" +native-tls = "0.2.14" oauth2 = "4.4.2" percent-encoding = "2.3.1" +postgres-native-tls = "0.5.1" rand = "0.8.5" reqwest = { version = "0.12.8", features = ["json"] } serde = { version = "1.0.213", features = ["derive"] } @@ -35,3 +38,4 @@ tracing = "0.1.40" tracing-subscriber = { version = "0.3.19", features = ["chrono", "env-filter"] } uuid = { version = "1.11.0", features = ["serde", "v4", "v7"] } validator = { version = "0.20.0", features = ["derive"] } +tokio-postgres = { version = "0.7.13", features = ["with-uuid-1", "with-chrono-0_4"] } diff --git a/README.md b/README.md new file mode 100644 index 0000000..76abe3f --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Interim + +A friendly, collaborative PostgreSQL front-end for nerds of all stripes. + +## Auth + +Internally, Interim controls authorization using the `SET ROLE` Postgres command to switch between users. This allows the Interim base user to impersonate roles it has created, without necessarily requiring Postgres superuser privileges itself (unlike if it were to use `SET SESSION AUTHORIZATION`). For each front-end user, Interim creates a Postgres role named (by default) `__interim_{account_uuid}`. diff --git a/catalogs/src/lib.rs b/catalogs/src/lib.rs deleted file mode 100644 index 5c0763a..0000000 --- a/catalogs/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod catalogs_schema; -pub mod pg_attribute; -pub mod pg_class; -pub mod pg_namespace; -pub mod pg_roles; -pub mod table_privileges; diff --git a/catalogs/src/pg_attribute.rs b/catalogs/src/pg_attribute.rs deleted file mode 100644 index f33b0a5..0000000 --- a/catalogs/src/pg_attribute.rs +++ /dev/null @@ -1,32 +0,0 @@ -use diesel::{ - dsl::{AsSelect, auto_type}, - pg::Pg, - prelude::*, -}; - -use crate::catalogs_schema::pg_attribute; - -pub use crate::catalogs_schema::pg_attribute::{dsl, table}; - -#[derive(Clone, Debug, Queryable, Selectable)] -#[diesel(table_name = pg_attribute)] -#[diesel(primary_key(attrelid, attname))] -pub struct PgAttribute { - pub attrelid: u32, - pub attname: String, - pub atttypid: u32, - pub attnum: i16, - pub attndims: i16, - pub attnotnull: bool, - pub atthasdef: bool, - pub attisdropped: bool, - pub attacl: Vec, -} - -impl PgAttribute { - #[auto_type(no_type_alias)] - pub fn all() -> _ { - let select: AsSelect = Self::as_select(); - table.select(select) - } -} diff --git a/catalogs/src/pg_class.rs b/catalogs/src/pg_class.rs deleted file mode 100644 index 8102401..0000000 --- a/catalogs/src/pg_class.rs +++ /dev/null @@ -1,25 +0,0 @@ -use diesel::{ - dsl::{AsSelect, auto_type}, - pg::Pg, - prelude::*, -}; - -use crate::catalogs_schema::pg_class; - -pub use crate::catalogs_schema::pg_class::{dsl, table}; - -#[derive(Clone, Debug, Queryable, Selectable)] -#[diesel(table_name = pg_class)] -#[diesel(primary_key(oid))] -pub struct PgClass { - pub oid: u32, - pub relname: String, -} - -impl PgClass { - #[auto_type(no_type_alias)] - pub fn all() -> _ { - let select: AsSelect = Self::as_select(); - table.select(select) - } -} diff --git a/catalogs/src/pg_namespace.rs b/catalogs/src/pg_namespace.rs deleted file mode 100644 index 0dd5a44..0000000 --- a/catalogs/src/pg_namespace.rs +++ /dev/null @@ -1,23 +0,0 @@ -use diesel::{ - dsl::{AsSelect, auto_type}, - pg::Pg, - prelude::*, -}; - -use crate::catalogs_schema::pg_namespace; - -pub use crate::catalogs_schema::pg_namespace::{dsl, table}; - -#[derive(Clone, Debug, Queryable, Selectable)] -#[diesel(table_name = pg_namespace)] -pub struct PgNamespace { - pub oid: u32, -} - -impl PgNamespace { - #[auto_type(no_type_alias)] - pub fn all() -> _ { - let select: AsSelect = Self::as_select(); - table.select(select) - } -} diff --git a/diesel.toml b/diesel.toml index a0d61bf..69e7f1d 100644 --- a/diesel.toml +++ b/diesel.toml @@ -2,7 +2,7 @@ # see https://diesel.rs/guides/configuring-diesel-cli [print_schema] -file = "src/schema.rs" +file = "do_not_use.txt" custom_type_derives = ["diesel::query_builder::QueryId", "Clone"] [migrations_directory] diff --git a/catalogs/Cargo.lock b/mdengine/Cargo.lock similarity index 100% rename from catalogs/Cargo.lock rename to mdengine/Cargo.lock diff --git a/catalogs/Cargo.toml b/mdengine/Cargo.toml similarity index 91% rename from catalogs/Cargo.toml rename to mdengine/Cargo.toml index 7635468..3548bd4 100644 --- a/catalogs/Cargo.toml +++ b/mdengine/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "catalogs" +name = "mdengine" version = "0.1.0" edition = "2024" diff --git a/catalogs/Makefile b/mdengine/Makefile similarity index 100% rename from catalogs/Makefile rename to mdengine/Makefile diff --git a/mdengine/README.md b/mdengine/README.md new file mode 100644 index 0000000..966d832 --- /dev/null +++ b/mdengine/README.md @@ -0,0 +1,7 @@ +# Interim Metadata Engine (mdengine) + +This crate is responsible for navigating the PostgreSQL `information_schema` +and catalogs tables to extract rich understanding of database structure and +permissions. + +It does not fetch data directly from any user defined tables. diff --git a/catalogs/diesel.toml b/mdengine/diesel.toml similarity index 100% rename from catalogs/diesel.toml rename to mdengine/diesel.toml diff --git a/mdengine/src/lib.rs b/mdengine/src/lib.rs new file mode 100644 index 0000000..def35db --- /dev/null +++ b/mdengine/src/lib.rs @@ -0,0 +1,41 @@ +use crate::{pg_class::PgClass, table_privileges::TablePrivilege}; +use diesel::{ + dsl::{AsSelect, auto_type}, + pg::Pg, + prelude::*, +}; + +pub mod pg_attribute; +pub mod pg_class; +pub mod pg_namespace; +pub mod pg_roles; +mod schema; +pub mod table_privileges; + +// Still waiting for Postgres to gain class consciousness +#[derive(Clone, Queryable, Selectable)] +pub struct PgClassPrivilege { + #[diesel(embed)] + pub class: PgClass, + #[diesel(embed)] + pub privilege: TablePrivilege, +} + +/// Query for the list of tables any of the provided roles has access to. A Vec +/// of grantees is accepted in case you wish to query for multiple roles of +/// which a user is a member. +#[auto_type(no_type_alias)] +pub fn class_privileges_for_grantees(grantees: Vec) -> _ { + let select: AsSelect = PgClassPrivilege::as_select(); + pg_class::table + .inner_join(pg_namespace::table.on(pg_namespace::dsl::oid.eq(pg_class::dsl::relnamespace))) + .inner_join( + table_privileges::table.on(table_privileges::dsl::table_schema + .eq(pg_namespace::dsl::nspname) + .and(table_privileges::dsl::table_name.eq(pg_class::dsl::relname))), + ) + // Excude indexes, series, etc. + .filter(pg_class::dsl::relkind.eq(b'r')) + .filter(table_privileges::dsl::grantee.eq_any(grantees)) + .select(select) +} diff --git a/mdengine/src/pg_attribute.rs b/mdengine/src/pg_attribute.rs new file mode 100644 index 0000000..bad182f --- /dev/null +++ b/mdengine/src/pg_attribute.rs @@ -0,0 +1,75 @@ +use diesel::{ + dsl::{AsSelect, auto_type}, + pg::Pg, + prelude::*, +}; + +use crate::schema::pg_attribute; + +pub use crate::schema::pg_attribute::{dsl, table}; + +#[derive(Clone, Debug, Queryable, Selectable)] +#[diesel(check_for_backend(Pg))] +#[diesel(table_name = pg_attribute)] +pub struct PgAttribute { + /// The table this column belongs to + pub attrelid: u32, + /// The column name + pub attname: String, + /// The data type of this column (zero for a dropped column) + pub atttypid: u32, + /// A copy of pg_type.typlen of this column's type + pub attlen: i16, + /// The number of the column. Ordinary columns are numbered from 1 up. System columns, such as ctid, have (arbitrary) negative numbers. + pub attnum: i16, + /// Always -1 in storage, but when loaded into a row descriptor in memory this might be updated to cache the offset of the attribute within the row + pub attcacheoff: i32, + /// atttypmod records type-specific data supplied at table creation time (for example, the maximum length of a varchar column). It is passed to type-specific input functions and length coercion functions. The value will generally be -1 for types that do not need atttypmod. + pub atttypmod: i32, + /// Number of dimensions, if the column is an array type; otherwise 0. (Presently, the number of dimensions of an array is not enforced, so any nonzero value effectively means “it's an array”.) + pub attndims: i16, + /// A copy of pg_type.typbyval of this column's type + pub attbyval: bool, + /// A copy of pg_type.typalign of this column's type + pub attalign: u8, + /// Normally a copy of pg_type.typstorage of this column's type. For TOAST-able data types, this can be altered after column creation to control storage policy. + pub attstorage: u8, + /// The current compression method of the column. Typically this is '\0' to specify use of the current default setting (see default_toast_compression). Otherwise, 'p' selects pglz compression, while 'l' selects LZ4 compression. However, this field is ignored whenever attstorage does not allow compression. + pub attcompression: Option, + /// This represents a not-null constraint. + pub attnotnull: bool, + /// This column has a default expression or generation expression, in which case there will be a corresponding entry in the pg_attrdef catalog that actually defines the expression. (Check attgenerated to determine whether this is a default or a generation expression.) + pub atthasdef: bool, + /// This column has a value which is used where the column is entirely missing from the row, as happens when a column is added with a non-volatile DEFAULT value after the row is created. The actual value used is stored in the attmissingval column. + pub atthasmissing: bool, + /// If a zero byte (''), then not an identity column. Otherwise, a = generated always, d = generated by default. + pub attidentity: Option, + /// If a zero byte (''), then not a generated column. Otherwise, s = stored. (Other values might be added in the future.) + pub attgenerated: Option, + /// This column has been dropped and is no longer valid. A dropped column is still physically present in the table, but is ignored by the parser and so cannot be accessed via SQL. + pub attisdropped: bool, + /// This column is defined locally in the relation. Note that a column can be locally defined and inherited simultaneously. + pub attislocal: bool, + /// The number of direct ancestors this column has. A column with a nonzero number of ancestors cannot be dropped nor renamed. + pub attinhcount: i16, + /// The defined collation of the column, or zero if the column is not of a collatable data type + pub attcollation: u32, + /// attstattarget controls the level of detail of statistics accumulated for this column by ANALYZE. A zero value indicates that no statistics should be collected. A null value says to use the system default statistics target. The exact meaning of positive values is data type-dependent. For scalar data types, attstattarget is both the target number of “most common values” to collect, and the target number of histogram bins to create. + pub attstattarget: Option, + /// Column-level access privileges, if any have been granted specifically on this column + pub attacl: Option>, + /// Attribute-level options, as “keyword=value” strings + pub attoptions: Option>, + /// Attribute-level foreign data wrapper options, as “keyword=value” strings + pub attfdwoptions: Option>, +} + +#[auto_type(no_type_alias)] +pub fn attributes_for_rel(oid: u32) -> _ { + let select: AsSelect = PgAttribute::as_select(); + pg_attribute::table + .filter(pg_attribute::dsl::attrelid.eq(oid)) + .filter(pg_attribute::dsl::attnum.gt(0i16)) + .filter(pg_attribute::dsl::attisdropped.eq(false)) + .select(select) +} diff --git a/mdengine/src/pg_class.rs b/mdengine/src/pg_class.rs new file mode 100644 index 0000000..c38c314 --- /dev/null +++ b/mdengine/src/pg_class.rs @@ -0,0 +1,14 @@ +use diesel::{pg::Pg, prelude::*}; + +use crate::schema::pg_class; + +pub use crate::schema::pg_class::{dsl, table}; + +#[derive(Clone, Debug, Queryable, Selectable)] +#[diesel(check_for_backend(Pg))] +#[diesel(table_name = pg_class)] +#[diesel(primary_key(oid))] +pub struct PgClass { + pub oid: u32, + pub relname: String, +} diff --git a/mdengine/src/pg_namespace.rs b/mdengine/src/pg_namespace.rs new file mode 100644 index 0000000..0285156 --- /dev/null +++ b/mdengine/src/pg_namespace.rs @@ -0,0 +1,13 @@ +use diesel::{pg::Pg, prelude::*}; + +use crate::schema::pg_namespace; + +pub use crate::schema::pg_namespace::{dsl, table}; + +#[derive(Clone, Debug, Queryable, Selectable)] +#[diesel(check_for_backend(Pg))] +#[diesel(table_name = pg_namespace)] +#[diesel(primary_key(oid))] +pub struct PgNamespace { + pub oid: u32, +} diff --git a/catalogs/src/pg_roles.rs b/mdengine/src/pg_roles.rs similarity index 61% rename from catalogs/src/pg_roles.rs rename to mdengine/src/pg_roles.rs index 812b7a3..252cd02 100644 --- a/catalogs/src/pg_roles.rs +++ b/mdengine/src/pg_roles.rs @@ -1,50 +1,39 @@ use chrono::{DateTime, Utc}; -use diesel::{ - dsl::{AsSelect, auto_type}, - pg::Pg, - prelude::*, -}; +use diesel::{pg::Pg, prelude::*}; -use crate::catalogs_schema::pg_roles; +use crate::schema::pg_roles; -pub use crate::catalogs_schema::pg_roles::{dsl, table}; +pub use crate::schema::pg_roles::{dsl, table}; #[derive(Clone, Debug, Queryable, Selectable)] +#[diesel(check_for_backend(Pg))] #[diesel(table_name = pg_roles)] #[diesel(primary_key(oid))] pub struct PgRole { /// Role name - rolname: String, + pub rolname: String, /// Role has superuser privileges - rolsuper: bool, + pub rolsuper: bool, /// Role automatically inherits privileges of roles it is a member of - rolinherit: bool, + pub rolinherit: bool, /// Role can create more roles - rolcreaterole: bool, + pub rolcreaterole: bool, /// Role can create databases - rolcreatedb: bool, + pub rolcreatedb: bool, /// Role can log in. That is, this role can be given as the initial session authorization identifier - rolcanlogin: bool, + pub rolcanlogin: bool, /// Role is a replication role. A replication role can initiate replication connections and create and drop replication slots. - rolreplication: bool, + pub rolreplication: bool, /// For roles that can log in, this sets maximum number of concurrent connections this role can make. -1 means no limit. - rolconnlimit: i32, + pub rolconnlimit: i32, /// Not the password (always reads as ********) - rolpassword: String, + pub rolpassword: String, /// Password expiry time (only used for password authentication); null if no expiration - rolvaliduntil: Option>, + pub rolvaliduntil: Option>, /// Role bypasses every row-level security policy, see Section 5.9 for more information. - rolbypassrls: bool, + pub rolbypassrls: bool, /// Role-specific defaults for run-time configuration variables - rolconfig: Option>, + pub rolconfig: Option>, /// ID of role - oid: u32, -} - -impl PgRole { - #[auto_type(no_type_alias)] - pub fn all() -> _ { - let select: AsSelect = Self::as_select(); - table.select(select) - } + pub oid: u32, } diff --git a/catalogs/src/catalogs_schema.rs b/mdengine/src/schema.rs similarity index 97% rename from catalogs/src/catalogs_schema.rs rename to mdengine/src/schema.rs index b2be7e9..3805f87 100644 --- a/catalogs/src/catalogs_schema.rs +++ b/mdengine/src/schema.rs @@ -133,7 +133,7 @@ table! { /// Normally a copy of pg_type.typstorage of this column's type. For TOAST-able data types, this can be altered after column creation to control storage policy. attstorage -> CChar, /// The current compression method of the column. Typically this is '\0' to specify use of the current default setting (see default_toast_compression). Otherwise, 'p' selects pglz compression, while 'l' selects LZ4 compression. However, this field is ignored whenever attstorage does not allow compression. - attcompression -> CChar, + attcompression -> Nullable, /// This represents a not-null constraint. attnotnull -> Bool, /// This column has a default expression or generation expression, in which case there will be a corresponding entry in the pg_attrdef catalog that actually defines the expression. (Check attgenerated to determine whether this is a default or a generation expression.) @@ -141,9 +141,9 @@ table! { /// This column has a value which is used where the column is entirely missing from the row, as happens when a column is added with a non-volatile DEFAULT value after the row is created. The actual value used is stored in the attmissingval column. atthasmissing -> Bool, /// If a zero byte (''), then not an identity column. Otherwise, a = generated always, d = generated by default. - attidentity -> CChar, + attidentity -> Nullable, /// If a zero byte (''), then not a generated column. Otherwise, s = stored. (Other values might be added in the future.) - attgenerated -> CChar, + attgenerated -> Nullable, /// This column has been dropped and is no longer valid. A dropped column is still physically present in the table, but is ignored by the parser and so cannot be accessed via SQL. attisdropped -> Bool, /// This column is defined locally in the relation. Note that a column can be locally defined and inherited simultaneously. @@ -153,13 +153,13 @@ table! { /// The defined collation of the column, or zero if the column is not of a collatable data type attcollation -> Oid, /// attstattarget controls the level of detail of statistics accumulated for this column by ANALYZE. A zero value indicates that no statistics should be collected. A null value says to use the system default statistics target. The exact meaning of positive values is data type-dependent. For scalar data types, attstattarget is both the target number of “most common values” to collect, and the target number of histogram bins to create. - attstattarget -> SmallInt, + attstattarget -> Nullable, /// Column-level access privileges, if any have been granted specifically on this column - attacl -> Array, + attacl -> Nullable>, /// Attribute-level options, as “keyword=value” strings - attoptions -> Array, + attoptions -> Nullable>, /// Attribute-level foreign data wrapper options, as “keyword=value” strings - attfdwoptions -> Array, + attfdwoptions -> Nullable>, } } diff --git a/catalogs/src/table_privileges.rs b/mdengine/src/table_privileges.rs similarity index 58% rename from catalogs/src/table_privileges.rs rename to mdengine/src/table_privileges.rs index 37e32b4..947eba9 100644 --- a/catalogs/src/table_privileges.rs +++ b/mdengine/src/table_privileges.rs @@ -1,38 +1,27 @@ -use diesel::{ - dsl::{AsSelect, auto_type}, - pg::Pg, - prelude::*, -}; +use diesel::{pg::Pg, prelude::*}; -use crate::catalogs_schema::table_privileges; +use crate::schema::table_privileges; -pub use crate::catalogs_schema::table_privileges::{dsl, table}; +pub use crate::schema::table_privileges::{dsl, table}; #[derive(Clone, Debug, Queryable, Selectable)] +#[diesel(check_for_backend(Pg))] #[diesel(table_name = table_privileges)] pub struct TablePrivilege { /// Name of the role that granted the privilege - grantor: String, + pub grantor: String, /// Name of the role that the privilege was granted to - grantee: String, + pub grantee: String, /// Name of the database that contains the table (always the current database) - table_catalog: String, + pub table_catalog: String, /// Name of the schema that contains the table - table_schema: String, + pub table_schema: String, /// Name of the table - table_name: String, + pub table_name: String, /// Type of the privilege: SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, or TRIGGER - privilege_type: String, + pub privilege_type: String, /// YES if the privilege is grantable, NO if not - is_grantable: String, + pub is_grantable: String, /// In the SQL standard, WITH HIERARCHY OPTION is a separate (sub-)privilege allowing certain operations on table inheritance hierarchies. In PostgreSQL, this is included in the SELECT privilege, so this column shows YES if the privilege is SELECT, else NO. - with_hierarchy: String, -} - -impl TablePrivilege { - #[auto_type(no_type_alias)] - pub fn all() -> _ { - let select: AsSelect = Self::as_select(); - table.select(select) - } + pub with_hierarchy: String, } diff --git a/src/abstract_.rs b/src/abstract_.rs index 4c21665..f4127cd 100644 --- a/src/abstract_.rs +++ b/src/abstract_.rs @@ -1,13 +1,7 @@ -use catalogs::{ - pg_class::{self, PgClass}, - pg_namespace, - table_privileges::{self, TablePrivilege}, -}; -use diesel::{ - dsl::{auto_type, AsSelect}, - pg::Pg, - prelude::*, -}; +use anyhow::{Context as _, Result}; +use diesel::{prelude::*, sql_query}; +use mdengine::pg_roles::{self, PgRole}; +use uuid::Uuid; pub fn escape_identifier(identifier: &str) -> String { // Escaping identifiers for Postgres is fairly easy, provided that the input is @@ -15,32 +9,33 @@ pub fn escape_identifier(identifier: &str) -> String { // remain as-is, and embedded double quotes are escaped simply by doubling // them (`"` becomes `""`). Refer to the PQescapeInternal() function in // libpq (fe-exec.c) and Diesel's PgQueryBuilder::push_identifier(). - // Here we also add spaces before and after the identifier quotes so that - // the identifier isn't inadvertently merged into a token immediately - // leading or following it. - format!(" \"{}\" ", identifier.replace('"', "\"\"")) + format!("\"{}\"", identifier.replace('"', "\"\"")) } -// Still waiting for Postgres to gain class consciousness -#[derive(Clone, Queryable, Selectable)] -pub struct PgClassPrivilege { - #[diesel(embed)] - pub class: PgClass, - #[diesel(embed)] - pub privilege: TablePrivilege, -} - -#[auto_type(no_type_alias)] -pub fn class_privileges_for_grantees(grantees: Vec) -> _ { - let select: AsSelect = PgClassPrivilege::as_select(); - pg_class::table - .inner_join(pg_namespace::table.on(pg_namespace::dsl::oid.eq(pg_class::dsl::relnamespace))) - .inner_join( - table_privileges::table.on(table_privileges::dsl::table_schema - .eq(pg_namespace::dsl::nspname) - .and(table_privileges::dsl::table_name.eq(pg_class::dsl::relname))), - ) - .filter(pg_class::dsl::relkind.eq(b'r')) - .filter(table_privileges::dsl::grantee.eq_any(grantees)) - .select(select) +pub fn diesel_set_user_id( + pg_user_role_prefix: &str, + user_id: Uuid, + conn: &mut PgConnection, +) -> Result<()> { + let role = pg_roles::table + .select(PgRole::as_select()) + .filter(pg_roles::dsl::rolname.eq(format!("{}{}", pg_user_role_prefix, user_id.simple()))) + .first(conn) + .optional() + .context("error reading role")?; + if role.is_none() { + sql_query(format!( + "CREATE ROLE {}", + escape_identifier(&format!("{}{}", pg_user_role_prefix, user_id.simple())) + )) + .execute(conn) + .context("error creating role")?; + } + sql_query(format!( + "SET ROLE {}", + escape_identifier(&format!("{}{}", pg_user_role_prefix, user_id.simple())) + )) + .execute(conn) + .context("error setting role to user")?; + Ok(()) } diff --git a/src/app_state.rs b/src/app_state.rs index 33516ef..25bdf34 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -5,19 +5,16 @@ use axum::{ extract::{FromRef, FromRequestParts}, http::request::Parts, }; -use deadpool_diesel::postgres::{Connection, Pool}; use oauth2::basic::BasicClient; -use crate::{ - abstract_::escape_identifier, app_error::AppError, auth, nav::NavbarBuilder, sessions::PgStore, - settings::Settings, -}; +use crate::{app_error::AppError, auth, nav::NavbarBuilder, sessions::PgStore, settings::Settings}; /// Global app configuration pub struct App { - pub db_pool: Pool, + pub diesel_pool: deadpool_diesel::postgres::Pool, pub navbar_template: NavbarBuilder, pub oauth_client: BasicClient, + pub pg_pool: deadpool_postgres::Pool, pub reqwest_client: reqwest::Client, pub session_store: PgStore, pub settings: Settings, @@ -27,30 +24,40 @@ impl App { /// Initialize global application functions based on config values pub async fn from_settings(settings: Settings) -> Result { let database_url = settings.database_url.clone(); - let manager = deadpool_diesel::postgres::Manager::from_config( - database_url, + let diesel_manager = deadpool_diesel::postgres::Manager::from_config( + database_url.clone(), deadpool_diesel::Runtime::Tokio1, deadpool_diesel::ManagerConfig { // Reset role after each interaction is recycled so that user // sessions remain isolated by deadpool interaction recycling_method: deadpool_diesel::RecyclingMethod::CustomQuery( - std::borrow::Cow::Owned(format!( - "SET ROLE {}", - escape_identifier(&settings.pg_root_role) - )), + std::borrow::Cow::Owned("RESET ROLE;".to_owned()), ), }, ); - let db_pool = deadpool_diesel::postgres::Pool::builder(manager).build()?; + let diesel_pool = deadpool_diesel::postgres::Pool::builder(diesel_manager).build()?; - let session_store = PgStore::new(db_pool.clone()); + let pg_config = deadpool_postgres::Config { + url: Some(database_url), + manager: Some(deadpool_postgres::ManagerConfig { + recycling_method: deadpool_postgres::RecyclingMethod::Clean, + }), + ..Default::default() + }; + let pg_pool = pg_config.create_pool( + Some(deadpool_postgres::Runtime::Tokio1), + postgres_native_tls::MakeTlsConnector::new(native_tls::TlsConnector::new()?), + )?; + + let session_store = PgStore::new(diesel_pool.clone()); let reqwest_client = reqwest::ClientBuilder::new().https_only(true).build()?; let oauth_client = auth::new_oauth_client(&settings)?; Ok(Self { - db_pool, + diesel_pool, navbar_template: NavbarBuilder::default().with_base_path(&settings.base_path), oauth_client, + pg_pool, reqwest_client, session_store, settings, @@ -74,17 +81,35 @@ where } } -/// Extractor to automatically obtain a Deadpool database connection -pub struct DbConn(pub Connection); +/// Extractor to automatically obtain a Deadpool Diesel connection +pub struct DieselConn(pub deadpool_diesel::postgres::Connection); -impl FromRequestParts for DbConn +impl FromRequestParts for DieselConn where S: Into + Clone + Sync, { type Rejection = AppError; async fn from_request_parts(_: &mut Parts, state: &S) -> Result { - let conn = Into::::into(state.clone()).db_pool.get().await?; + let conn = Into::::into(state.clone()) + .diesel_pool + .get() + .await?; + Ok(Self(conn)) + } +} + +/// Extractor to automatically obtain a Deadpool tokio-postgres connection +pub struct PgConn(pub deadpool_postgres::Object); + +impl FromRequestParts for PgConn +where + S: Into + Clone + Sync, +{ + type Rejection = AppError; + + async fn from_request_parts(_: &mut Parts, state: &S) -> Result { + let conn = Into::::into(state.clone()).pg_pool.get().await?; Ok(Self(conn)) } } diff --git a/src/data_layer.rs b/src/data_layer.rs new file mode 100644 index 0000000..b950b6b --- /dev/null +++ b/src/data_layer.rs @@ -0,0 +1,118 @@ +use std::fmt::Display; + +use anyhow::Result; +use chrono::{DateTime, Utc}; +use deadpool_postgres::tokio_postgres::{ + row::RowIndex, + types::{FromSql, Type}, + Row, +}; +use derive_builder::Builder; +use uuid::Uuid; + +pub enum Contents { + Text(String), + Integer(i32), + Timestamptz(DateTime), + Uuid(Uuid), +} + +pub struct Value(Option); + +#[derive(Builder)] +#[builder(pattern = "owned", setter(prefix = "with"))] +pub struct FieldOptions { + /// Format with which to render timestamptz values + #[builder(default = "\"%Y-%m-%dT%H:%M:%S%.f%:z\".to_owned()")] + pub date_format: String, + + /// If some, treat text column like an enum + #[builder(default)] + pub select_options: Option>, + + /// Text to display in place of actual column name + #[builder(default)] + pub label: Option, + + #[builder(default = "true")] + pub editable: bool, +} + +pub trait ToHtmlString { + fn to_html_string(&self, options: &FieldOptions) -> String; +} + +#[derive(Clone, Debug)] +pub struct FromSqlError { + message: String, +} + +impl std::error::Error for FromSqlError {} + +impl Display for FromSqlError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.message) + } +} + +impl FromSqlError { + fn new(message: &str) -> Self { + Self { + message: message.to_owned(), + } + } +} + +impl Value { + pub fn get_from_row(row: &Row, idx: I) -> Result { + Ok(Self(row.try_get::<_, Option>(idx)?)) + } +} + +impl ToHtmlString for Value { + fn to_html_string(&self, options: &FieldOptions) -> String { + if let Self(Some(contents)) = self { + match contents { + Contents::Text(value) => value.clone(), + &Contents::Integer(value) => value.to_string(), + &Contents::Timestamptz(value) => value.format(&options.date_format).to_string(), + &Contents::Uuid(value) => format!( + "{}", + value.hyphenated() + ), + } + } else { + "NULL".to_owned() + } + } +} + +impl<'a> FromSql<'a> for Contents { + fn from_sql( + ty: &Type, + raw: &'a [u8], + ) -> Result> { + match ty { + &Type::INT4 => Ok(Self::Integer(i32::from_sql(ty, raw)?)), + &Type::TEXT | &Type::VARCHAR => Ok(Self::Text(String::from_sql(ty, raw)?)), + &Type::TIMESTAMPTZ => Ok(Self::Timestamptz(DateTime::::from_sql(ty, raw)?)), + &Type::UUID => Ok(Self::Uuid(::from_sql(ty, raw)?)), + _ => Err(Box::new(FromSqlError::new( + "unsupported pg type for interim Value", + ))), + } + } + + fn accepts(ty: &Type) -> bool { + matches!(ty, &Type::TEXT | &Type::VARCHAR | &Type::UUID) + } +} + +pub struct Lens { + pub fields: Vec, +} + +pub struct Field { + pub options: FieldOptions, + pub name: String, +} diff --git a/src/main.rs b/src/main.rs index 5e51c97..61c78ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ mod app_error; mod app_state; mod auth; mod cli; +mod data_layer; mod middleware; mod migrations; mod nav; @@ -40,7 +41,7 @@ async fn main() { if settings.run_database_migrations == Some(1) { // Run migrations on server startup - let conn = state.db_pool.get().await.unwrap(); + let conn = state.diesel_pool.get().await.unwrap(); conn.interact(|conn| conn.run_pending_migrations(MIGRATIONS).and(Ok(()))) .await .unwrap() diff --git a/src/router.rs b/src/router.rs index 069a778..59fae9c 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,19 +1,20 @@ use anyhow::{Context as _, Result}; use askama::Template; use axum::{ - extract::State, + extract::{Path, State}, http::{header::CACHE_CONTROL, HeaderValue}, response::{Html, IntoResponse as _, Response}, routing::get, Router, }; -use catalogs::{ +use deadpool_postgres::{tokio_postgres::Row, GenericClient}; +use diesel::prelude::*; +use mdengine::{ + class_privileges_for_grantees, + pg_attribute::{attributes_for_rel, PgAttribute}, pg_class::{self, PgClass}, - pg_namespace, - pg_roles::{self, PgRole}, - table_privileges::{self, TablePrivilege}, }; -use diesel::{prelude::*, sql_query}; +use serde::Deserialize; use tower::ServiceBuilder; use tower_http::{ services::{ServeDir, ServeFile}, @@ -21,18 +22,22 @@ use tower_http::{ }; use crate::{ - abstract_::{class_privileges_for_grantees, escape_identifier}, + abstract_::{diesel_set_user_id, escape_identifier}, app_error::AppError, - app_state::{AppState, DbConn}, + app_state::{AppState, DieselConn, PgConn}, auth, + data_layer::{Field, FieldOptionsBuilder, ToHtmlString as _, Value}, settings::Settings, users::CurrentUser, }; +const FRONTEND_ROW_LIMIT: i64 = 1000; + pub fn new_router(state: AppState) -> Router<()> { let base_path = state.settings.base_path.clone(); let app = Router::new() .route("/", get(landing_page)) + .route("/c/{oid}/viewer", get(viewer_page)) .nest("/auth", auth::new_router()) .layer(SetResponseHeaderLayer::if_not_present( CACHE_CONTROL, @@ -71,7 +76,7 @@ async fn landing_page( pg_user_role_prefix, .. }): State, - DbConn(db_conn): DbConn, + DieselConn(db_conn): DieselConn, CurrentUser(current_user): CurrentUser, ) -> Result { let grantees = vec![format!( @@ -80,38 +85,8 @@ async fn landing_page( current_user.id.simple() )]; let visible_tables = db_conn - .interact(move |conn| -> Result> { - let role = PgRole::all() - .filter(pg_roles::dsl::rolname.eq(format!( - "{}{}", - pg_user_role_prefix, - current_user.id.simple() - ))) - .first(conn) - .optional() - .context("error reading role")?; - if role.is_none() { - sql_query(format!( - "CREATE ROLE {}", - escape_identifier(&format!( - "{}{}", - pg_user_role_prefix, - current_user.id.simple() - )) - )) - .execute(conn) - .context("error creating role")?; - } - sql_query(format!( - "SET ROLE {}", - escape_identifier(&format!( - "{}{}", - pg_user_role_prefix, - current_user.id.simple() - )) - )) - .execute(conn) - .context("error setting role to user")?; + .interact(move |conn| -> Result> { + diesel_set_user_id(&pg_user_role_prefix, current_user.id, conn)?; let privileges = class_privileges_for_grantees(grantees) .load(conn) .context("error reading classes")?; @@ -134,3 +109,91 @@ async fn landing_page( ) .into_response()) } + +#[derive(Deserialize)] +struct ViewerPagePath { + oid: u32, +} + +async fn viewer_page( + State(Settings { + base_path, + pg_user_role_prefix, + .. + }): State, + DieselConn(diesel_conn): DieselConn, + PgConn(pg_client): PgConn, + CurrentUser(current_user): CurrentUser, + Path(params): Path, +) -> Result { + pg_client + .query( + &format!( + "SET ROLE {};", + escape_identifier(&format!( + "{}{}", + pg_user_role_prefix, + current_user.id.simple() + )), + ), + &[], + ) + .await?; + + // FIXME: Ensure user has access to relation + + // One-off helper struct to hold Diesel results + struct RelMeta { + class: PgClass, + attrs: Vec, + } + let RelMeta { class, attrs } = diesel_conn + .interact(move |conn| -> Result<_> { + Ok(RelMeta { + class: pg_class::table + .filter(pg_class::dsl::oid.eq(params.oid)) + .select(PgClass::as_select()) + .first(conn)?, + attrs: attributes_for_rel(params.oid).load(conn)?, + }) + }) + .await + .unwrap()?; + let query = [ + "SELECT", + &attrs + .iter() + .map(|attr| attr.attname.clone()) + .collect::>() + .join(", "), + "FROM", + &escape_identifier(&class.relname), + "LIMIT", + &FRONTEND_ROW_LIMIT.to_string(), + ";", + ] + .join(" "); + let rows = pg_client.query(&query, &[]).await?; + #[derive(Template)] + #[template(path = "class-viewer.html")] + struct ResponseTemplate { + base_path: String, + fields: Vec, + rows: Vec, + } + Ok(Html( + ResponseTemplate { + base_path, + fields: attrs + .into_iter() + .map(|attr| Field { + options: FieldOptionsBuilder::default().build().unwrap(), + name: attr.attname, + }) + .collect(), + rows, + } + .render()?, + ) + .into_response()) +} diff --git a/src/users.rs b/src/users.rs index ad8114b..5918f86 100644 --- a/src/users.rs +++ b/src/users.rs @@ -104,7 +104,7 @@ where format!("{}/auth/login", app_state.settings.base_path), )); }; - let db_conn = app_state.db_pool.get().await?; + let db_conn = app_state.diesel_pool.get().await?; let current_user = db_conn .interact(move |conn| { let maybe_current_user = User::all() diff --git a/templates/base.html b/templates/base.html index ed4843c..4ff9cd2 100644 --- a/templates/base.html +++ b/templates/base.html @@ -6,7 +6,6 @@ - {% include "nav.html" %} {% block main %}{% endblock main %} diff --git a/templates/tmp.html b/templates/tmp.html index 24d82b2..ac7f0ce 100644 --- a/templates/tmp.html +++ b/templates/tmp.html @@ -5,12 +5,14 @@ Name + OID {% for relation in relations %} {{ relation.relname }} + {{ relation.oid }} {% endfor %}