diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..39a27e5 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4179 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "arc-swap" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a07d1f37ff60921c83bdfc7407723bdefe89b44b98a9b772f225c8f9d67141a6" +dependencies = [ + "rustversion", +] + +[[package]] +name = "arcstr" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03918c3dbd7701a85c6b9887732e2921175f26c350b4563841d0958c21d57e6d" + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "aws-lc-rs" +version = "1.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "axum" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "backon" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" +dependencies = [ + "fastrand", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-modes" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2211b0817f061502a8dd9f11a37e879e79763e3c698d2418cf824d8cb2f21e" + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.2.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.0", +] + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "destructure_traitobject" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" + +[[package]] +name = "diesel" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e7624a3bb9fffd82fff016be9a7f163d20e5a89eb8d28f9daaa6b30fff37500" +dependencies = [ + "bitflags", + "byteorder", + "chrono", + "diesel_derives", + "downcast-rs", + "itoa", + "pq-sys", + "r2d2", + "serde_json", +] + +[[package]] +name = "diesel_derives" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9daac6489a36e42570da165a10c424f3edcefdff70c5fd55e1847c23f3dd7562" +dependencies = [ + "diesel_table_macro_syntax", + "dsl_auto_type", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe2444076b48641147115697648dc743c2c00b61adade0f01ce67133c7babe8c" +dependencies = [ + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "downcast-rs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" + +[[package]] +name = "dsl_auto_type" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd122633e4bef06db27737f21d3738fb89c8f6d5360d6d9d7635dda142a7757e" +dependencies = [ + "darling", + "either", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "htycommons" +version = "0.1.0" +source = "git+https://github.com/alchemy-studio/AuthCore.git#5a050a8fbe9b44f43e728c7be8ebbcb34e7fb4c2" +dependencies = [ + "aes", + "anyhow", + "axum", + "base64", + "block-modes", + "cbc", + "chrono", + "data-encoding", + "diesel", + "dotenv", + "hex", + "hex-literal", + "jsonwebtoken 9.3.1", + "log", + "log4rs", + "rand 0.9.2", + "redis 0.24.0", + "reqwest 0.12.24", + "ring", + "rust-crypto", + "serde", + "serde_derive", + "serde_json", + "thiserror 2.0.17", + "time 0.3.44", + "tokio", + "tracing", + "tracing-appender", + "tracing-subscriber", + "uuid", +] + +[[package]] +name = "htykc" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "axum-macros", + "diesel", + "dotenv", + "htycommons", + "htykc_models", + "htyuc_models", + "htyuc_remote", + "serde", + "serde_json", + "tokio", + "tower-http", + "tracing", +] + +[[package]] +name = "htykc_models" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "diesel", + "htycommons", + "htyuc_models", + "htyws_models", + "jsonwebtoken 10.2.0", + "log", + "log4rs", + "rand 0.10.0", + "reqwest 0.13.2", + "ring", + "rust-crypto", + "serde", + "serde_derive", + "serde_json", + "string-builder", + "thiserror 2.0.17", + "time 0.3.44", + "tokio", + "tower-http", + "tracing", + "tracing-appender", + "tracing-subscriber", + "uuid", +] + +[[package]] +name = "htyproc" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "chrono", + "dotenv", + "htycommons", + "htyts_models", + "redis 1.1.0", + "reqwest 0.13.2", + "serde", + "serde_json", + "tokio", + "tower-http", + "tracing", + "uuid", +] + +[[package]] +name = "htyts" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "chrono", + "diesel", + "dotenv", + "htycommons", + "htyts_models", + "once_cell", + "redis 1.1.0", + "reqwest 0.13.2", + "serde", + "serde_json", + "tokio", + "tower-http", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "htyts_models" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "diesel", + "htycommons", + "serde", + "serde_json", +] + +[[package]] +name = "htyuc_models" +version = "0.1.0" +source = "git+https://github.com/alchemy-studio/AuthCore.git#5a050a8fbe9b44f43e728c7be8ebbcb34e7fb4c2" +dependencies = [ + "anyhow", + "chrono", + "diesel", + "htycommons", + "log", + "reqwest 0.12.24", + "serde", + "serde_json", + "tokio", + "tracing", +] + +[[package]] +name = "htyuc_remote" +version = "0.1.0" +source = "git+https://github.com/alchemy-studio/AuthCore.git#5a050a8fbe9b44f43e728c7be8ebbcb34e7fb4c2" +dependencies = [ + "aes", + "anyhow", + "base64", + "block-modes", + "chrono", + "data-encoding", + "diesel", + "dotenv", + "hex", + "hex-literal", + "htycommons", + "htyuc_models", + "jsonwebtoken 9.3.1", + "log", + "log4rs", + "rand 0.9.2", + "reqwest 0.12.24", + "ring", + "rust-crypto", + "serde", + "serde_derive", + "serde_json", + "thiserror 2.0.17", + "time 0.3.44", + "tokio", + "tower-http", + "tracing", + "tracing-appender", + "tracing-subscriber", + "uuid", +] + +[[package]] +name = "htyws" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "axum-macros", + "chrono", + "diesel", + "dotenv", + "htycommons", + "htyuc_models", + "htyuc_remote", + "htyws_models", + "reqwest 0.13.2", + "serde", + "serde_json", + "time 0.3.44", + "tokio", + "tower-http", + "tracing", +] + +[[package]] +name = "htyws_models" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "diesel", + "htycommons", + "htyuc_models", + "serde", + "serde_derive", + "serde_json", + "tokio", + "tracing", +] + +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.1", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "jsonwebtoken" +version = "10.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c76e1c7d7df3e34443b3621b459b066a7b79644f059fc8b2db7070c825fd417e" +dependencies = [ + "base64", + "ed25519-dalek", + "getrandom 0.2.16", + "hmac", + "js-sys", + "p256", + "p384", + "pem", + "rand 0.8.5", + "rsa", + "serde", + "serde_json", + "sha2", + "signature", + "simple_asn1", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +dependencies = [ + "serde", +] + +[[package]] +name = "log-mdc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" + +[[package]] +name = "log4rs" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e947bb896e702c711fccc2bf02ab2abb6072910693818d1d6b07ee2b9dfd86c" +dependencies = [ + "anyhow", + "arc-swap", + "chrono", + "derive_more", + "fnv", + "humantime", + "libc", + "log", + "log-mdc", + "mock_instant", + "parking_lot", + "rand 0.9.2", + "serde", + "serde-value", + "serde_json", + "serde_yaml", + "thiserror 2.0.17", + "thread-id", + "typemap-ors", + "unicode-segmentation", + "winapi", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.61.2", +] + +[[package]] +name = "mock_instant" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce6dd36094cac388f119d2e9dc82dc730ef91c32a6222170d630e5414b956e6" + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe 0.1.6", + "openssl-sys", + "schannel", + "security-framework 2.11.1", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64", + "serde_core", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "pq-sys" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "574ddd6a267294433f140b02a726b0640c43cf7c6f717084684aaa3b285aba61" +dependencies = [ + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2 0.6.1", + "thiserror 2.0.17", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.17", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.1", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "r2d2" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.0", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "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]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "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.4", +] + +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redis" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c580d9cbbe1d1b479e8d67cf9daf6a62c957e6846048408b80b43ac3f6af84cd" +dependencies = [ + "combine", + "itoa", + "percent-encoding", + "ryu", + "sha1_smol", + "socket2 0.4.10", + "url", +] + +[[package]] +name = "redis" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e41a79ae5cbb41257d84cf4cf0db0bb5a95b11bf05c62c351de4fe748620d" +dependencies = [ + "arc-swap", + "arcstr", + "async-lock", + "backon", + "bytes", + "cfg-if", + "combine", + "futures-channel", + "futures-util", + "itoa", + "num-bigint", + "percent-encoding", + "pin-project-lite", + "ryu", + "sha1_smol", + "socket2 0.6.1", + "tokio", + "tokio-util", + "url", + "xxhash-rust", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "reqwest" +version = "0.12.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +dependencies = [ + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time 0.1.45", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc-serialize" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe 0.2.1", + "rustls-pki-types", + "schannel", + "security-framework 3.5.1", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework 3.5.1", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.17", + "time 0.3.44", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "string-builder" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd10a070fb1f2796a288abec42695db4682a82b6f12ffacd60fb8d5ad3a4a12" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread-id" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99043e46c5a15af379c06add30d9c93a6c0e8849de00d244c4a2c417da128d80" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.1", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror 1.0.69", + "time 0.3.44", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "time 0.3.44", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typemap-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867" +dependencies = [ + "unsafe-any-ors", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unsafe-any-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad" +dependencies = [ + "destructure_traitobject", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "getrandom 0.3.4", + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen 0.46.0", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6e74db2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,48 @@ +[workspace] +members = [ + "htyws", + "htyws_models", + "htykc_models", + "htykc", + "htyts_models", + "htyts", + "htyproc", +] +resolver = "2" + +[workspace.package] +version = "0.1.0" +authors = ["阿男 ", "buddy"] +edition = "2021" + +[workspace.dependencies] +htycommons = { git = "https://github.com/alchemy-studio/AuthCore.git" } +htyuc_models = { git = "https://github.com/alchemy-studio/AuthCore.git" } +htyuc_remote = { git = "https://github.com/alchemy-studio/AuthCore.git" } +anyhow = "^1.0" +axum = { version = "^0.8" } +axum-macros = "^0.5" +chrono = { version = "^0.4", features = ["serde"] } +diesel = { version = "^2.2", features = ["postgres", "r2d2", "serde_json", "chrono"] } +dotenv = "^0.15" +jsonwebtoken = { version = "^10.2", features = ["rust_crypto"] } +log = "^0.4" +log4rs = "^1.2" +rand = "^0.10" +reqwest = { version = "^0.13", features = ["blocking", "json"] } +ring = "^0.17" +rust-crypto = "*" +serde = { version = "^1.0", features = ["derive"] } +serde_derive = "^1.0" +serde_json = "^1.0" +thiserror = "^2.0" +time = { version = "^0.3", features = ["macros"] } +tokio = { version = "^1.47", features = ["full"] } +tower-http = { version = "^0.6", features = ["trace"] } +tracing = { version = "^0.1" } +tracing-appender = "^0.2" +tracing-subscriber = { version = "^0.3", features = ["env-filter", "local-time"] } +uuid = { version = "^1.16", features = ["serde", "v4"] } +url = "^2.5" +string-builder = "*" +redis = { version = "1.1", features = ["tokio-comp", "connection-manager"] } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0ac8b66 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +# todo: 继承centos-powertools +# 安装rustup, cargo, diesel +# ^ 这些可以放在centos-powertools里面 +# 可以参考htyuc/htyws已有的Dockerfile diff --git a/README.md b/README.md new file mode 100644 index 0000000..2c1afc6 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +☕️ + +[![CI](https://github.com/alchemy-studio/huiwing/actions/workflows/ci.yml/badge.svg)](https://github.com/alchemy-studio/huiwing/actions) + + + +🚧 本仓库不要提交大文件 + +🚧 本仓库请使用`Pull Request`的形式交代码 + +🚧 `.env`文件只对本地测试有效,部署时使用`docker-compose.yml`整体启动. + + +## 目录 + +### htyws +web service,容器 `ws`,端口`3000`,对外不暴露 + +### user-center +用户中心,容器 `uc`,端口`3001`,对外不暴露 + +### htymusic +音乐教室小程序,容器名`mr`,端口`8010:80` + +### htycommons +run test: +`$ print_debug=true cargo test -- --test-threads=1 --nocapture` + +--- + +```bash +postgres=# create user htyuc; +CREATE ROLE +postgres=# create user htyws; +CREATE ROLE +postgres=# create database htyuc_local owner htyuc encoding utf8; +CREATE DATABASE +postgres=# create database htyws_local owner htyws encoding utf8; +CREATE DATABASE +postgres=# +``` diff --git a/cp_envs_huiwings.sh b/cp_envs_huiwings.sh new file mode 100755 index 0000000..94bf67a --- /dev/null +++ b/cp_envs_huiwings.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -x + +cp envs/huiwings/htyuc.env htyuc/.env +cp envs/huiwings/htyuc.env htyuc_models/.env +cp envs/huiwings/htyws.env htyws/.env +cp envs/huiwings/htyws.env htyws_models/.env +cp envs/huiwings/htykc.env htykc/.env +cp envs/huiwings/htykc.env htykc_models/.env +cp envs/huiwings/htyts.env htyts/.env +cp envs/huiwings/htyproc.env htyproc/.env diff --git a/cp_envs_local_macos.sh b/cp_envs_local_macos.sh new file mode 100755 index 0000000..1aceada --- /dev/null +++ b/cp_envs_local_macos.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -x + +cp envs/local_macos/htyuc.env htyuc/.env +cp envs/local_macos/htyuc.env htyuc_models/.env +cp envs/local_macos/htyws.env htyws/.env +cp envs/local_macos/htyws.env htyws_models/.env +cp envs/local_macos/htykc.env htykc/.env +cp envs/local_macos/htykc.env htykc_models/.env +cp envs/local_macos/htyts.env htyts/.env +cp envs/local_macos/htyproc.env htyproc/.env diff --git a/cp_envs_moicen.sh b/cp_envs_moicen.sh new file mode 100755 index 0000000..7a28e12 --- /dev/null +++ b/cp_envs_moicen.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -x + +cp envs/moicen/htyuc.env htyuc/.env +cp envs/moicen/htyuc.env htyuc_models/.env +cp envs/moicen/htyws.env htyws/.env +cp envs/moicen/htyws.env htyws_models/.env +cp envs/moicen/htykc.env htykc/.env +cp envs/moicen/htykc.env htykc_models/.env +cp envs/moicen/htyts.env htyts/.env +cp envs/moicen/htyproc.env htyproc/.env diff --git a/docker-compose.authcore-e2e.yml b/docker-compose.authcore-e2e.yml new file mode 100644 index 0000000..ab1fd1b --- /dev/null +++ b/docker-compose.authcore-e2e.yml @@ -0,0 +1,44 @@ +# HTYTS + AuthCore HTYUC 本地联调:双 Postgres + Redis(端口与 `scripts/run-authcore-e2e-docker.sh` 一致)。 +# 宿主机端口:15432 htyts、15433 htyuc、16379 redis,避免与本地 PostgreSQL/Redis 冲突。 +# +# docker compose -f docker-compose.authcore-e2e.yml up -d --wait +# ./scripts/run-authcore-e2e-docker.sh + +services: + authcore-e2e-pg-ts: + image: postgres:14 + environment: + POSTGRES_USER: htyts_e2e + POSTGRES_PASSWORD: htyts_e2e + POSTGRES_DB: htyts_e2e + ports: + - "15432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U htyts_e2e -d htyts_e2e"] + interval: 3s + timeout: 5s + retries: 20 + + authcore-e2e-pg-uc: + image: postgres:14 + environment: + POSTGRES_USER: htyuc + POSTGRES_PASSWORD: htyuc + POSTGRES_DB: htyuc_test + ports: + - "15433:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U htyuc -d htyuc_test"] + interval: 3s + timeout: 5s + retries: 20 + + authcore-e2e-redis: + image: redis:7 + ports: + - "16379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 3s + timeout: 5s + retries: 20 diff --git a/docker-compose.ts-e2e.yml b/docker-compose.ts-e2e.yml new file mode 100644 index 0000000..468f66e --- /dev/null +++ b/docker-compose.ts-e2e.yml @@ -0,0 +1,33 @@ +# PostgreSQL + Redis for htyts integration / e2e tests. +# Aligns with AuthCore `docker-compose.test.yml` (postgres:14, redis:7), separate ports to avoid clashes. +# +# Schema: `htyts_models/migrations/*` (Diesel). Run `diesel migration run` after `up` — see `scripts/run-ts-e2e.sh`. +# +# Usage: +# docker compose -f docker-compose.ts-e2e.yml up -d --wait +# ./scripts/run-ts-e2e.sh +# +services: + ts-e2e-db: + image: postgres:14 + environment: + POSTGRES_USER: htyts_e2e + POSTGRES_PASSWORD: htyts_e2e + POSTGRES_DB: htyts_e2e + ports: + - "5436:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U htyts_e2e -d htyts_e2e"] + interval: 3s + timeout: 5s + retries: 10 + + ts-e2e-redis: + image: redis:7 + ports: + - "6390:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 3s + timeout: 5s + retries: 10 diff --git a/docs/integration_cutover.md b/docs/integration_cutover.md new file mode 100644 index 0000000..50c158c --- /dev/null +++ b/docs/integration_cutover.md @@ -0,0 +1,64 @@ +# 联调与切流(htyts / htyproc) + +## 环境变量 + +### htyts(任务 API) + +与 `htyts::config` 一致,典型项包括:`TS_DATABASE_URL`、Redis(`htycommons::redis_util` 所读变量)、`TS_PORT`、`task_server.zombie_min`(或等价)、JWT/密钥类变量与现有 `htyws`/`htykc` 对齐。课程调度:`TS_SUDOER_TOKEN`、`TS_DOMAIN`、`HTYKC_URL` 等。 + +### htyproc(执行器) + +| 变量 | 说明 | +|------|------| +| `TS_URL` | 任务服务基址(含协议与端口),如 `http://127.0.0.1:8080` | +| `TS_DOMAIN` | `HtyHost` 语义,与现网域名一致 | +| `NGX_URL` | OpenResty/Nginx 基址,用于 `combine` / `convert` 等 | +| `PROC_SUDOER_TOKEN` | 调 TS 与 Ngx 的 sudoer JWT(与 Java proc 一致) | +| `PROC_PORT` | 控制面 HTTP 端口,默认 `8081` | +| `WAIT_SEC` / `task_server.wait_sec` | 轮询间隔秒数,默认 `5` | +| `MAX_PROCESSING_TASKS` | 并发执行上限,默认 `20` | +| Redis | 与 TS 相同实例;`htycommons::redis_util::get_redis_url()` | + +## Nginx 示例 + +将原指向 Java `task_server` / `proc_server` 的 `location` 改为 Rust 进程 upstream(端口按部署调整): + +```nginx +upstream htyts_backend { + server 127.0.0.1:8080; +} +upstream htyproc_backend { + server 127.0.0.1:8081; +} + +location /api/v1/ts/ { + proxy_pass http://htyts_backend; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +} + +location /api/v1/proc/ { + proxy_pass http://htyproc_backend; + proxy_set_header Host $host; +} +``` + +切流前在**并行端口**上跑 `htyts`/`htyproc`,用 curl 或 `htymusic`/`htyadmin` 指向测试域名验证后再改 `proxy_pass`。 + +## 联调要点(htymusic / htyadmin) + +- 创建/更新任务、`one_pending_task` 拉取、任务状态回写与 Redis payload 一致。 +- 课程通知:`/api/v1/ts/kc/*` 启停与状态与运维脚本一致。 +- proc:`GET /api/v1/proc/start|stop|status` 行为与 Java 一致后再切换 upstream。`status` 载荷中的 `status` 字段与 Java `ProcessorHelper.Status` 一致:`PENDING` / `RUNNING` / `ABORT` / `ERROR`。 + +## 监控与下线 Java 条件建议 + +- 指标:`one_zombie_task` 数量、任务 `FAILED` 比例、proc 日志中 `update_task` 失败率、Redis `TS_*` 键与 DB 行一致性抽检。 +- 条件:在目标环境**至少一个完整业务周期**内无回归;TS/proc 错误率与 Java 基线可比或更好;回滚方案(保留 Java 二进制与旧 upstream 块)已验证。 + +## htyts 本地 E2E(Docker + cargo test) + +- Compose:`huiwing/docker-compose.ts-e2e.yml`(Postgres 14、Redis 7;端口 `5436` / `6390`,避免与 AuthCore `docker-compose.test.yml` 的 `5433` / `6380` 冲突)。 +- 建表:`huiwing/htyts_models/migrations/`,本地/CI 用 `diesel migration run`(与 `htyuc_models` 相同流程)。 +- 一键脚本:`huiwing/scripts/run-ts-e2e.sh` — `up --wait`、清空 `dbtask` 与 Redis、`POOL_SIZE`/`JWT_KEY`/DB URL 等环境变量后执行 `cargo test -p htyts --test ts_e2e_http`。 +- `HtySudoerToken` 校验依赖 Redis 中 `HW_T_{token_id}` 与 JWT 一致;测试内会写入与 AuthCore `verify_jwt` 相同的约定。 diff --git a/docs/ts_phase_a_inventory.md b/docs/ts_phase_a_inventory.md new file mode 100644 index 0000000..cb0f991 --- /dev/null +++ b/docs/ts_phase_a_inventory.md @@ -0,0 +1,58 @@ +# 阶段 A 契约清单(task_server / proc / task_commons → Rust) + +本文档与 `htyts_models`、`htyts` crate 一并落地,供后续阶段 B/C 实现与现网对齐时对照。 + +## PostgreSQL:`DbTask` 表 + +Java 实体:仓库根目录下 [`task_commons/.../DbTask.java`](../../task_commons/src/main/java/cn/alchemystudio/taskcommons/db/DbTask.java)。 + +- **表名(已核对):** 正式机 `alchemy-studio.cn` 上任务库为 `htytask_alchemy`、`htytask_huiwings`(`moicen` 等环境库名可能为 `htytask_moicen`,以该机 `\l` 为准)。`public` 下表名为 **`dbtask`**(非 `db_task`),与 [`htyts/src/schema.rs`](../htyts/src/schema.rs) 一致。 +- **核对命令(需本机可 `ssh weli@alchemy-studio.cn` 且 `sudo -u postgres` 免密):** `sudo -n -u postgres psql -d htytask_alchemy -c '\d+ public.dbtask'`。直连 `psql -U postgres` 可能要求密码,可改用上述 `sudo`。 +- **已记录的列类型(`htytask_alchemy.public.dbtask`):** `task_id varchar(255)` PK;`task_type` / `task_status` / `created_by` / `updated_by` `varchar(255)`;`hty_id` / `meta` 可空;`meta varchar(65535)`;`duration` `double precision`;`created_at` / `updated_at` `timestamp(6) without time zone`。 +- **列(与 Java 字段一致):** `task_id` (PK), `hty_id`, `task_type`, `task_status`, `duration`, `created_by`, `created_at`, `updated_by`, `updated_at`, `meta`。 +- **枚举存字符串:** 与 Java `Enum.name()` 一致,如 `PENDING`, `UPLOAD_PICTURE`。Rust 侧解析见 `htyts_models::TaskStatus::from_str` / `TaskType::from_str`。 + +参考 DDL(**以现网为准**): + +```sql +-- VERIFY table name and types against production before use. +CREATE TABLE IF NOT EXISTS dbtask ( + task_id TEXT PRIMARY KEY, + hty_id TEXT, + task_type TEXT NOT NULL, + task_status TEXT NOT NULL, + duration DOUBLE PRECISION, + created_by TEXT NOT NULL, + created_at TIMESTAMP NOT NULL, + updated_by TEXT NOT NULL, + updated_at TIMESTAMP NOT NULL, + meta TEXT +); +``` + +## Redis + +- Java:`task_commons` `BaseRedisService.TS_REDIS_PREFIX` = `"TS_"`;任务 payload 为 `TS_{task_id}` 的字符串值(JSON)。 +- Rust:`htyts_models::task_payload_redis_key` / `TS_REDIS_PREFIX`。 +- **Sudoer 缓存(与 AuthCore `HW_T_` 不同):** Java `task_server` 的 `CheckAuthFilter` 使用 `TS_SUDO_T_{token_id}` 存 `HtyToken` JSON(见 `RedisTokenCacheService`)。Rust `htyts` 的 `create_task` / `update_task` 已按该语义校验(可选走 `HTYUC_URL` + `/api/v1/uc/verify_jwt_token`,环境变量 `AUTH_CHECKING` / `TOKEN_VERIFY` / `EXP_DAYS` 与 Java 对应)。 +- 与 `htycommons::redis_util` 中 `HW_`、`T_` 等前缀**不要混用**到任务 payload;sudoer 校验路径以 `task_server` 为准。 + +## HTTP 契约(后续阶段实现) + +- **Task service** 前缀:`/api/v1/ts`(见 Java `TaskApp` + `TaskService`)。 +- **Proc 控制面:** `/api/v1/proc/start|stop|status`。 +- **Ts 客户端(proc → ts):** `TsProxy` 路径 `create_task`, `update_task`, `one_pending_task`, `one_zombie_task`。 +- **AI 客户端:** `AiProxy` `/api/v1/ai` 下 `compare`, `watermark`, `compress`, `get_task_result`。 + +## 与 AuthCore 的复用(proc 侧) + +以下类型在 Java 中来自 `task_commons`,Rust 侧 **优先使用 AuthCore 已有定义**: + +- `ReqHtyResource`、`ReqRefResource` 等:在 **`htycommons::models`**(见 `htyuc_models` 对 `ReqHtyResource` 的引用与 `HtyResource::to_req`)。 +- 实现 proc 前请在 `AuthCore` 中 `grep` 上述类型名,避免在 `htyts_models` 重复定义。 + +## JSON 字段名 + +- `ReqTasksWithPage` 使用 `totalPage`(Java bean)。 +- `AudioFileAndAiScorePayload` 使用 `aiScorePayload` / `audioPayload`(已在 `htyts_models` 标注 `serde(rename = ...)`)。 +- 其它 payload 与 `CommonTaskResult` 若与现网抓包不一致,以 **实际响应** 为准并补 `serde` 别名。 diff --git a/envs/huiwings/htykc.env b/envs/huiwings/htykc.env new file mode 100644 index 0000000..6224483 --- /dev/null +++ b/envs/huiwings/htykc.env @@ -0,0 +1,46 @@ +#DATABASE_URL=postgres://huiwings:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyuc_huiwings +#UC_DB_URL=postgres://huiwings:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyuc_huiwings +#WS_DB_URL=postgres://huiwings:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyws_huiwings + +UC_DB_URL=postgres://postgres:postgres@localhost:5432/htyuc_huiwings +WS_DB_URL=postgres://postgres:postgres@localhost:5432/htyws_huiwings +KC_DB_URL=postgres://postgres:postgres@localhost:5432/htykc_huiwings +DATABASE_URL=postgres://postgres:postgres@localhost:5432/htykc_huiwings + +ADMIN_DOMAIN=admin.huiwings.cn +EXPIRATION_DAYS=10 +JWT_KEY=0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE +LOGGER_LEVEL=DEBUG +MUSIC_ROOM_MINI_DOMAIN=music-room.huiwings.cn +MUSIC_ROOM_MINI_URL=https://music-room.huiwings.cn +MUSIC_ROOM_WX_MINI_ID=wx3b02c886bb554012 +MUSIC_ROOM_WX_MINI_SECRET=b78a2a3d6b66634993a919a4293d1675 +POOL_SIZE=20 +REDIS_HOST=localhost +REDIS_PORT=6379 +SKIP_POST_LOGIN=false +SKIP_REGISTRATION=false +SKIP_WX_PUSH=false +TS_DOMAIN=ts.huiwings.cn + +UC_PORT=3000 +UC_URL=http://127.0.0.1:3000/api/v1/uc + +UPYUN_OPERATOR=moicen +UPYUN_PASSWORD=NyJ51zRwFApY9Wo9EHJMrb8GI9YtvpVN + +WS_URL=http://127.0.0.1:3000/api/v1/ws +WS_PORT=3001 + +KC_URL=http://127.0.0.1:3002/api/v1/kc +KC_PORT=3002 + +WX_MSG_REJECT_REGISTER="LcOF7w9k6gCunk2iY5myjxrDyo832dsU5_xJmEicZCs" +WX_MSG_STUDENT_REGISTER="KIHfyzqTU6IiCuDfrvGfz9112eKWv4FDOv43wyxcsJc" +WX_MSG_STUDENT_REGISTER_SUCCESS="uYl6_GGDKJaXY8VgIQlrpPZASOW8lIlHgDYmtE2Mgp8" +WX_MSG_TEACHER_REGISTER="KIHfyzqTU6IiCuDfrvGfz9112eKWv4FDOv43wyxcsJc" +WX_MSG_TEACHER_REGISTER_SUCCESS="jGmcJZB--IAlvKS3D0a5elvdrx_X6ImAMlvmjrg254A" + +NGX_URL=https://admin.huiwings.cn/api/ngx + +print_debug=true \ No newline at end of file diff --git a/envs/huiwings/htyproc.env b/envs/huiwings/htyproc.env new file mode 100644 index 0000000..9cf841a --- /dev/null +++ b/envs/huiwings/htyproc.env @@ -0,0 +1,16 @@ +# htyproc — 对齐 task_server/scripts/huiwings/run_proc.sh;HTTP 基址无 /api/v1 后缀 +# 任务库名对齐 Java -DDBNAME=htytask_huiwings;htyproc 进程不连库,仅与迁移/运维一致 +TS_DATABASE_URL=postgres://postgres:postgres@localhost:5432/htytask_huiwings + +TS_URL=http://127.0.0.1:3003 +HTYUC_URL=http://127.0.0.1:3000 +HTYWS_URL=http://127.0.0.1:3001 +AI_URL=https://ai.alchemy-studio.cn +TS_DOMAIN=ts.huiwings.cn +NGX_URL=https://admin.huiwings.cn/api/ngx + +REDIS_HOST=localhost +REDIS_PORT=6379 +LOGGER_LEVEL=DEBUG +PROC_PORT=3004 +WAIT_SEC=5 diff --git a/envs/huiwings/htyts.env b/envs/huiwings/htyts.env new file mode 100644 index 0000000..23c4eea --- /dev/null +++ b/envs/huiwings/htyts.env @@ -0,0 +1,19 @@ +# htyts — 对齐 task_server/scripts/huiwings/run_ts.sh(-DDBNAME=htytask_huiwings)与 htyts::config +TS_DATABASE_URL=postgres://postgres:postgres@localhost:5432/htytask_huiwings +DATABASE_URL=postgres://postgres:postgres@localhost:5432/htytask_huiwings + +JWT_KEY=0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE +POOL_SIZE=20 +REDIS_HOST=localhost +REDIS_PORT=6379 + +TS_DOMAIN=ts.huiwings.cn +HTYUC_URL=http://127.0.0.1:3000 +HTYKC_URL=http://127.0.0.1:3002 +TS_PORT=3003 + +TOKEN_VERIFY=true +AUTH_CHECKING=true +EXP_DAYS=1 +ZOMBIE_MIN=30 +LOGGER_LEVEL=DEBUG diff --git a/envs/huiwings/htyuc.env b/envs/huiwings/htyuc.env new file mode 100644 index 0000000..437e374 --- /dev/null +++ b/envs/huiwings/htyuc.env @@ -0,0 +1,44 @@ +#DATABASE_URL=postgres://huiwings:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyuc_huiwings +#UC_DB_URL=postgres://huiwings:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyuc_huiwings +#WS_DB_URL=postgres://huiwings:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyws_huiwings + +UC_DB_URL=postgres://postgres:postgres@localhost:5432/htyuc_huiwings +WS_DB_URL=postgres://postgres:postgres@localhost:5432/htyws_huiwings +KC_DB_URL=postgres://postgres:postgres@localhost:5432/htykc_huiwings +DATABASE_URL=postgres://postgres:postgres@localhost:5432/htyuc_huiwings + +ADMIN_DOMAIN=admin.huiwings.cn +EXPIRATION_DAYS=10 +JWT_KEY=0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE +LOGGER_LEVEL=DEBUG +MUSIC_ROOM_MINI_DOMAIN=music-room.huiwings.cn +MUSIC_ROOM_MINI_URL=https://music-room.huiwings.cn +MUSIC_ROOM_WX_MINI_ID=wx3b02c886bb554012 +MUSIC_ROOM_WX_MINI_SECRET=b78a2a3d6b66634993a919a4293d1675 +POOL_SIZE=20 +REDIS_HOST=localhost +REDIS_PORT=6379 +SKIP_POST_LOGIN=false +SKIP_REGISTRATION=false +SKIP_WX_PUSH=false +TS_DOMAIN=ts.huiwings.cn +UC_PORT=3000 +UC_URL=http://127.0.0.1:3000/api/v1/uc +UPYUN_OPERATOR=moicen +UPYUN_PASSWORD=NyJ51zRwFApY9Wo9EHJMrb8GI9YtvpVN + +WS_URL=http://127.0.0.1:3000/api/v1/ws +WS_PORT=3001 + +KC_URL=http://127.0.0.1:3002/api/v1/kc +KC_PORT=3002 + +WX_MSG_REJECT_REGISTER="LcOF7w9k6gCunk2iY5myjxrDyo832dsU5_xJmEicZCs" +WX_MSG_STUDENT_REGISTER="KIHfyzqTU6IiCuDfrvGfz9112eKWv4FDOv43wyxcsJc" +WX_MSG_STUDENT_REGISTER_SUCCESS="uYl6_GGDKJaXY8VgIQlrpPZASOW8lIlHgDYmtE2Mgp8" +WX_MSG_TEACHER_REGISTER="KIHfyzqTU6IiCuDfrvGfz9112eKWv4FDOv43wyxcsJc" +WX_MSG_TEACHER_REGISTER_SUCCESS="jGmcJZB--IAlvKS3D0a5elvdrx_X6ImAMlvmjrg254A" + +NGX_URL=https://admin.huiwings.cn/api/ngx + +print_debug=true \ No newline at end of file diff --git a/envs/huiwings/htyws.env b/envs/huiwings/htyws.env new file mode 100644 index 0000000..9eedc84 --- /dev/null +++ b/envs/huiwings/htyws.env @@ -0,0 +1,31 @@ +#DATABASE_URL=postgres://huiwings:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyws_huiwings +#UC_DB_URL=postgres://huiwings:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyuc_huiwings +#WS_DB_URL=postgres://huiwings:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyws_huiwings + +UC_DB_URL=postgres://postgres:postgres@localhost:5432/htyuc_huiwings +WS_DB_URL=postgres://postgres:postgres@localhost:5432/htyws_huiwings +KC_DB_URL=postgres://postgres:postgres@localhost:5432/htykc_huiwings +DATABASE_URL=postgres://postgres:postgres@localhost:5432/htyws_huiwings +JWT_KEY=0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE +LOGGER_LEVEL=DEBUG +POOL_SIZE=20 +REDIS_HOST=localhost +REDIS_PORT=6379 +SKIP_POST_LOGIN=false +SKIP_REGISTRATION=false +SKIP_WX_PUSH=false +UC_PORT=3000 +UC_URL=http://127.0.0.1:3000/api/v1/uc + +WS_PORT=3001 +WS_URL=/api/v1/ws + +KC_URL=http://127.0.0.1:3002/api/v1/kc +KC_PORT=3002 + +WX_MSG_PIYUE_DONE="qbaILdUCOSFQP6mMyU90eL20OHbusBynbtW5MKbzPn4" +WX_MSG_PUSH_MSG_STUDENT="eYN9SE_zENqjIzWtMFan12gqYQRpSAoGUTxkyNxMTbA" +WX_MSG_PUSH_MSG_TEACHER="_jQIfMlKlSC0nm3ddaApEcZANJkhh_am3lKnaoqnGQE" +WX_MSG_COMMENT="vZDsTwie42G4W_MaqeZ5P8NYqn8TxrRnoyoEaXQlzXs" + +NGX_URL=https://admin.huiwings.cn/api/ngx diff --git a/envs/local_macos/htykc.env b/envs/local_macos/htykc.env new file mode 100644 index 0000000..216ebff --- /dev/null +++ b/envs/local_macos/htykc.env @@ -0,0 +1,42 @@ +ADMIN_DOMAIN=admin.localhost:8088 +DATABASE_URL=postgres://htykc@localhost/htykc_local +EXPIRATION_DAYS=1 +JWT_KEY=0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE +LOGGER_LEVEL=debug +MUSIC_ROOM_MINI_DOMAIN=music-room.localhost +MUSIC_ROOM_MINI_URL=https://music-room.localhost +MUSIC_ROOM_WX_MINI_ID=wx3b02c886bb554012 +MUSIC_ROOM_WX_MINI_SECRET=b78a2a3d6b66634993a919a4293d1675 +POOL_SIZE=20 +REDIS_HOST=localhost +REDIS_PORT=6379 +SKIP_POST_LOGIN=true +SKIP_REGISTRATION=true +SKIP_WX_PUSH=true +TS_DOMAIN=ts.localhost +UC_DB_URL=postgres://htyuc@localhost/htyuc_local +UC_PORT=3000 +UC_URL=http://127.0.0.1:3000/api/v1/uc +UPYUN_OPERATOR=moicen +UPYUN_PASSWORD=NyJ51zRwFApY9Wo9EHJMrb8GI9YtvpVN +WS_DB_URL=postgres://htyws@localhost/htyws_local + +WS_PORT=3001 +WS_URL=http://127.0.0.1:3000/api/v1/ws + +KC_DB_URL=postgres://htykc@localhost/htykc_local +KC_URL=http://127.0.0.1:3002/api/v1/kc +KC_PORT=3002 + +#WX_MP_DOMAIN=wx.localhost +#WX_MP_ID=wx30919c9c9bb48e44 +#WX_MP_SECRET=a993780f38b74d8541f1a9e90411c1d1 +WX_MSG_REJECT_REGISTER="V6jMcj2l-mrQRz9a0GW0EiB4va6WCyrpk2wyfAkAmW0" +WX_MSG_STUDENT_REGISTER="BkiGoXhrLJoXJB-wn50wgHfKrqQL0mgMycORcurJDi8" +WX_MSG_STUDENT_REGISTER_SUCCESS="OqI9IIFwiJners5bToVpborGIvQZbDhIcCSvlaJm2OI" +WX_MSG_TEACHER_REGISTER="BkiGoXhrLJoXJB-wn50wgHfKrqQL0mgMycORcurJDi8" +WX_MSG_TEACHER_REGISTER_SUCCESS="ocVsZ1RmE3UmGbQk3YHAp6_DAoLi5Qw2aF3PzpEGp_E" + +NGX_URL=http://127.0.0.1:3000/api/ngx + +print_debug=true diff --git a/envs/local_macos/htyproc.env b/envs/local_macos/htyproc.env new file mode 100644 index 0000000..1c3d10d --- /dev/null +++ b/envs/local_macos/htyproc.env @@ -0,0 +1,15 @@ +# 任务库名对齐本地 htyts;htyproc 进程不连库 +TS_DATABASE_URL=postgres://htyws@localhost/htytask_local + +TS_URL=http://127.0.0.1:3003 +HTYUC_URL=http://127.0.0.1:3000 +HTYWS_URL=http://127.0.0.1:3001 +AI_URL=http://127.0.0.1:5000 +TS_DOMAIN=ts.localhost +NGX_URL=http://127.0.0.1:3000/api/ngx + +REDIS_HOST=localhost +REDIS_PORT=6379 +LOGGER_LEVEL=debug +PROC_PORT=3004 +WAIT_SEC=5 diff --git a/envs/local_macos/htyts.env b/envs/local_macos/htyts.env new file mode 100644 index 0000000..bde186a --- /dev/null +++ b/envs/local_macos/htyts.env @@ -0,0 +1,19 @@ +# htyts — 本地 task 库名需与 Postgres 中实际库一致(可先 createdb) +TS_DATABASE_URL=postgres://htyws@localhost/htytask_local +DATABASE_URL=postgres://htyws@localhost/htytask_local + +JWT_KEY=0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE +POOL_SIZE=20 +REDIS_HOST=localhost +REDIS_PORT=6379 + +TS_DOMAIN=ts.localhost +HTYUC_URL=http://127.0.0.1:3000 +HTYKC_URL=http://127.0.0.1:3002 +TS_PORT=3003 + +TOKEN_VERIFY=true +AUTH_CHECKING=true +EXP_DAYS=1 +ZOMBIE_MIN=30 +LOGGER_LEVEL=debug diff --git a/envs/local_macos/htyuc.env b/envs/local_macos/htyuc.env new file mode 100644 index 0000000..44d567a --- /dev/null +++ b/envs/local_macos/htyuc.env @@ -0,0 +1,42 @@ +ADMIN_DOMAIN=admin.localhost:8088 +DATABASE_URL=postgres://htyuc@localhost/htyuc_local +EXPIRATION_DAYS=1 +JWT_KEY=0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE +LOGGER_LEVEL=debug +MUSIC_ROOM_MINI_DOMAIN=music-room.localhost +MUSIC_ROOM_MINI_URL=https://music-room.localhost +MUSIC_ROOM_WX_MINI_ID=wx3b02c886bb554012 +MUSIC_ROOM_WX_MINI_SECRET=b78a2a3d6b66634993a919a4293d1675 +POOL_SIZE=20 +REDIS_HOST=localhost +REDIS_PORT=6379 +SKIP_POST_LOGIN=true +SKIP_REGISTRATION=true +SKIP_WX_PUSH=true +TS_DOMAIN=ts.localhost +UC_DB_URL=postgres://htyuc@localhost/htyuc_local +UC_PORT=3000 +UC_URL=http://127.0.0.1:3000/api/v1/uc +UPYUN_OPERATOR=moicen +UPYUN_PASSWORD=NyJ51zRwFApY9Wo9EHJMrb8GI9YtvpVN +WS_DB_URL=postgres://htyws@localhost/htyws_local + +WS_URL=http://127.0.0.1:3000/api/v1/ws +WS_PORT=3001 + +KC_DB_URL=postgres://htykc@localhost/htykc_local +KC_URL=http://127.0.0.1:3002/api/v1/kc +KC_PORT=3002 + +#WX_MP_DOMAIN=wx.localhost +#WX_MP_ID=wx30919c9c9bb48e44 +#WX_MP_SECRET=a993780f38b74d8541f1a9e90411c1d1 +WX_MSG_REJECT_REGISTER="V6jMcj2l-mrQRz9a0GW0EiB4va6WCyrpk2wyfAkAmW0" +WX_MSG_STUDENT_REGISTER="BkiGoXhrLJoXJB-wn50wgHfKrqQL0mgMycORcurJDi8" +WX_MSG_STUDENT_REGISTER_SUCCESS="OqI9IIFwiJners5bToVpborGIvQZbDhIcCSvlaJm2OI" +WX_MSG_TEACHER_REGISTER="BkiGoXhrLJoXJB-wn50wgHfKrqQL0mgMycORcurJDi8" +WX_MSG_TEACHER_REGISTER_SUCCESS="ocVsZ1RmE3UmGbQk3YHAp6_DAoLi5Qw2aF3PzpEGp_E" + +NGX_URL=http://127.0.0.1:3000/api/ngx + +print_debug=true diff --git a/envs/local_macos/htyws.env b/envs/local_macos/htyws.env new file mode 100644 index 0000000..c5ffbf1 --- /dev/null +++ b/envs/local_macos/htyws.env @@ -0,0 +1,25 @@ +DATABASE_URL=postgres://htyws@localhost/htyws_local +JWT_KEY=0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE +LOGGER_LEVEL=debug +POOL_SIZE=20 +REDIS_HOST=localhost +REDIS_PORT=6379 +SKIP_POST_LOGIN=true +SKIP_REGISTRATION=true +SKIP_WX_PUSH=true +UC_DB_URL=postgres://htyuc@localhost/htyuc_local +UC_PORT=3000 +UC_URL=http://127.0.0.1:3000/api/v1/uc +WS_DB_URL=postgres://htyws@localhost/htyws_local + +WS_PORT=3001 +WS_URL=/api/v1/ws + +KC_DB_URL=postgres://htykc@localhost/htykc_local +KC_URL=http://127.0.0.1:3002/api/v1/kc +KC_PORT=3002 + +WX_MSG_PIYUE_DONE="qbaILdUCOSFQP6mMyU90eL20OHbusBynbtW5MKbzPn4" +WX_MSG_PUSH_MSG_STUDENT="eYN9SE_zENqjIzWtMFan12gqYQRpSAoGUTxkyNxMTbA" +WX_MSG_PUSH_MSG_TEACHER="_jQIfMlKlSC0nm3ddaApEcZANJkhh_am3lKnaoqnGQE" +WX_MSG_COMMENT="PLACEHOLDER" diff --git a/envs/moicen/htykc.env b/envs/moicen/htykc.env new file mode 100644 index 0000000..680d1ec --- /dev/null +++ b/envs/moicen/htykc.env @@ -0,0 +1,47 @@ +#UC_DB_URL=postgres://alchemy:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyuc_moicen +#WS_DB_URL=postgres://alchemy:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyws_moicen +#DATABASE_URL=postgres://alchemy:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyuc_moicen + +UC_DB_URL=postgres://postgres:postgres@localhost:5432/htyuc_moicen +WS_DB_URL=postgres://postgres:postgres@localhost:5432/htyws_moicen +KC_DB_URL=postgres://postgres:postgres@localhost:5432/htykc_moicen +DATABASE_URL=postgres://postgres:postgres@localhost:5432/htykc_moicen + +ADMIN_DOMAIN=admin.moicen.com +EXPIRATION_DAYS=1 +JWT_KEY=0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE +LOGGER_LEVEL=DEBUG +MUSIC_ROOM_MINI_DOMAIN=music-room.moicen.com +MUSIC_ROOM_MINI_URL=https://music-room.moicen.com +MUSIC_ROOM_WX_MINI_ID=wx3b02c886bb554012 +MUSIC_ROOM_WX_MINI_SECRET=b78a2a3d6b66634993a919a4293d1675 +POOL_SIZE=20 +REDIS_HOST=localhost +REDIS_PORT=6379 +SKIP_POST_LOGIN=false +SKIP_REGISTRATION=false +SKIP_WX_PUSH=false +TS_DOMAIN=ts.moicen.com +UC_PORT=3000 +UC_URL=http://127.0.0.1:3000/api/v1/uc +UPYUN_OPERATOR=moicen +UPYUN_PASSWORD=NyJ51zRwFApY9Wo9EHJMrb8GI9YtvpVN + +WS_URL=http://127.0.0.1:3000/api/v1/ws +WS_PORT=3001 + +KC_URL=http://127.0.0.1:3002/api/v1/kc +KC_PORT=3002 + +#WX_MP_DOMAIN=wx.alchemy-studio.cn +#WX_MP_ID=wxfd3381693a9a3e70 +#WX_MP_SECRET=5a221a62baf1868a495345da1673732f +WX_MSG_REJECT_REGISTER="LcOF7w9k6gCunk2iY5myjxrDyo832dsU5_xJmEicZCs" +WX_MSG_STUDENT_REGISTER="KIHfyzqTU6IiCuDfrvGfz9112eKWv4FDOv43wyxcsJc" +WX_MSG_STUDENT_REGISTER_SUCCESS="uYl6_GGDKJaXY8VgIQlrpPZASOW8lIlHgDYmtE2Mgp8" +WX_MSG_TEACHER_REGISTER="KIHfyzqTU6IiCuDfrvGfz9112eKWv4FDOv43wyxcsJc" +WX_MSG_TEACHER_REGISTER_SUCCESS="jGmcJZB--IAlvKS3D0a5elvdrx_X6ImAMlvmjrg254A" + +NGX_URL=https://admin.moicen.com/api/ngx + +print_debug=true diff --git a/envs/moicen/htyproc.env b/envs/moicen/htyproc.env new file mode 100644 index 0000000..0f44293 --- /dev/null +++ b/envs/moicen/htyproc.env @@ -0,0 +1,15 @@ +# 任务库名对齐 Java -DDBNAME=htytask_moicen;htyproc 进程不连库 +TS_DATABASE_URL=postgres://postgres:postgres@localhost:5432/htytask_moicen + +TS_URL=http://127.0.0.1:3003 +HTYUC_URL=http://127.0.0.1:3000 +HTYWS_URL=http://127.0.0.1:3001 +AI_URL=https://ai.moicen.com +TS_DOMAIN=ts.moicen.com +NGX_URL=https://admin.moicen.com/api/ngx + +REDIS_HOST=localhost +REDIS_PORT=6379 +LOGGER_LEVEL=DEBUG +PROC_PORT=3004 +WAIT_SEC=5 diff --git a/envs/moicen/htyts.env b/envs/moicen/htyts.env new file mode 100644 index 0000000..f18d00a --- /dev/null +++ b/envs/moicen/htyts.env @@ -0,0 +1,19 @@ +# htyts — 对齐 task_server/scripts/moicen/run_ts.sh(-DDBNAME=htytask_moicen) +TS_DATABASE_URL=postgres://postgres:postgres@localhost:5432/htytask_moicen +DATABASE_URL=postgres://postgres:postgres@localhost:5432/htytask_moicen + +JWT_KEY=0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE +POOL_SIZE=20 +REDIS_HOST=localhost +REDIS_PORT=6379 + +TS_DOMAIN=ts.moicen.com +HTYUC_URL=http://127.0.0.1:3000 +HTYKC_URL=http://127.0.0.1:3002 +TS_PORT=3003 + +TOKEN_VERIFY=true +AUTH_CHECKING=true +EXP_DAYS=1 +ZOMBIE_MIN=30 +LOGGER_LEVEL=DEBUG diff --git a/envs/moicen/htyuc.env b/envs/moicen/htyuc.env new file mode 100644 index 0000000..4cc6f03 --- /dev/null +++ b/envs/moicen/htyuc.env @@ -0,0 +1,47 @@ +#UC_DB_URL=postgres://alchemy:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyuc_moicen +#WS_DB_URL=postgres://alchemy:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyws_moicen +#DATABASE_URL=postgres://alchemy:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyuc_moicen + +UC_DB_URL=postgres://postgres:postgres@localhost:5432/htyuc_moicen +WS_DB_URL=postgres://postgres:postgres@localhost:5432/htyws_moicen +KC_DB_URL=postgres://postgres:postgres@localhost:5432/htykc_moicen +DATABASE_URL=postgres://postgres:postgres@localhost:5432/htyuc_moicen + +ADMIN_DOMAIN=admin.moicen.com +EXPIRATION_DAYS=1 +JWT_KEY=0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE +LOGGER_LEVEL=DEBUG +MUSIC_ROOM_MINI_DOMAIN=music-room.moicen.com +MUSIC_ROOM_MINI_URL=https://music-room.moicen.com +MUSIC_ROOM_WX_MINI_ID=wx3b02c886bb554012 +MUSIC_ROOM_WX_MINI_SECRET=b78a2a3d6b66634993a919a4293d1675 +POOL_SIZE=20 +REDIS_HOST=localhost +REDIS_PORT=6379 +SKIP_POST_LOGIN=false +SKIP_REGISTRATION=false +SKIP_WX_PUSH=false +TS_DOMAIN=ts.moicen.com +UC_PORT=3000 +UC_URL=http://127.0.0.1:3000/api/v1/uc +UPYUN_OPERATOR=moicen +UPYUN_PASSWORD=NyJ51zRwFApY9Wo9EHJMrb8GI9YtvpVN + +WS_URL=http://127.0.0.1:3000/api/v1/ws +WS_PORT=3001 + +KC_URL=http://127.0.0.1:3002/api/v1/kc +KC_PORT=3002 + +#WX_MP_DOMAIN=wx.alchemy-studio.cn +#WX_MP_ID=wxfd3381693a9a3e70 +#WX_MP_SECRET=5a221a62baf1868a495345da1673732f +WX_MSG_REJECT_REGISTER="LcOF7w9k6gCunk2iY5myjxrDyo832dsU5_xJmEicZCs" +WX_MSG_STUDENT_REGISTER="KIHfyzqTU6IiCuDfrvGfz9112eKWv4FDOv43wyxcsJc" +WX_MSG_STUDENT_REGISTER_SUCCESS="uYl6_GGDKJaXY8VgIQlrpPZASOW8lIlHgDYmtE2Mgp8" +WX_MSG_TEACHER_REGISTER="KIHfyzqTU6IiCuDfrvGfz9112eKWv4FDOv43wyxcsJc" +WX_MSG_TEACHER_REGISTER_SUCCESS="jGmcJZB--IAlvKS3D0a5elvdrx_X6ImAMlvmjrg254A" + +NGX_URL=https://admin.moicen.com/api/ngx + +print_debug=true diff --git a/envs/moicen/htyws.env b/envs/moicen/htyws.env new file mode 100644 index 0000000..741e5c4 --- /dev/null +++ b/envs/moicen/htyws.env @@ -0,0 +1,35 @@ +#UC_DB_URL=postgres://alchemy:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyuc_moicen +#WS_DB_URL=postgres://alchemy:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyws_moicen +#DATABASE_URL=postgres://alchemy:ngK_D8WV!Wi_@bj-postgres-qe58e4r0.sql.tencentcdb.com:28154/htyws_moicen + +UC_DB_URL=postgres://postgres:postgres@localhost:5432/htyuc_moicen +WS_DB_URL=postgres://postgres:postgres@localhost:5432/htyws_moicen +KC_DB_URL=postgres://postgres:postgres@localhost:5432/htykc_moicen +DATABASE_URL=postgres://postgres:postgres@localhost:5432/htyws_moicen + +JWT_KEY=0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE0xCAFEBABE +LOGGER_LEVEL=DEBUG +POOL_SIZE=20 +REDIS_HOST=localhost +REDIS_PORT=6379 +SKIP_POST_LOGIN=false +SKIP_REGISTRATION=false +SKIP_WX_PUSH=false +UC_PORT=3000 +UC_URL=http://127.0.0.1:3000/api/v1/uc + +WS_PORT=3001 +WS_URL=/api/v1/ws + +KC_URL=http://127.0.0.1:3002/api/v1/kc +KC_PORT=3002 + +WX_MSG_PIYUE_DONE="mP2JdR2HMfz_7P-HYSJJADzjAb08qH7WzlR3CGnMKwI" +# 创建计划 +WX_MSG_PUSH_MSG_STUDENT="pJH8umvYm8lftDpXNdBSkeKOHQXGtlFISh6wAEgHwPQ" +# 添加练习 +WX_MSG_PUSH_MSG_TEACHER="1G4sPsWMYrdUEgqXi5mfMP1utpQ0xVSBpIx7ZBTF0MA" +# 评论 +WX_MSG_COMMENT="MYrBrHZWE6IQPtOQl7mIzCoSv9fn8SIw9DT--xvkIMI" + +NGX_URL=https://admin.moicen.com/api/ngx \ No newline at end of file diff --git a/htycompose/docker-compose.yml b/htycompose/docker-compose.yml new file mode 100644 index 0000000..001b4fe --- /dev/null +++ b/htycompose/docker-compose.yml @@ -0,0 +1,32 @@ +version: "3.8" +services: + htymusic: + image: ghcr.io/alchemy-studio/htymusic + container_name: htymusic + tty: true + ports: + - "8010:8010" + environment: + WS_PORT: 3000 + UC_PORT: 3001 + depends_on: + - htyws + - htyuc + htyws: + image: ghcr.io/alchemy-studio/htyws + container_name: htyws + tty: true + ports: + - "3000:3000" + environment: + ROCKET_PORT: 3000 + DATABASE_URL: postgres://htyws@mydb/htyws + htyuc: + image: ghcr.io/alchemy-studio/htyuc + container_name: htyuc + tty: true + ports: + - "3001:3001" + environment: + ROCKET_PORT: 3001 + DATABASE_URL: postgres://htyuc@mydb/htyuc diff --git a/htykc/Cargo.toml b/htykc/Cargo.toml new file mode 100644 index 0000000..b69c473 --- /dev/null +++ b/htykc/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "htykc" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +htycommons = { workspace = true } +htyuc_models = { workspace = true } +htyuc_remote = { workspace = true } +htykc_models = { path = "../htykc_models" } +anyhow = { workspace = true } +axum = { workspace = true } +axum-macros = { workspace = true } +diesel = { workspace = true } +dotenv = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true } +tower-http = { workspace = true } +tracing = { workspace = true } diff --git a/htykc/src/lib.rs b/htykc/src/lib.rs new file mode 100644 index 0000000..1e38bef --- /dev/null +++ b/htykc/src/lib.rs @@ -0,0 +1,96 @@ +use crate::ws_kecheng::*; +use crate::ws_repeat_kecheng::{create_kecheng_repeat, update_kecheng_repeat}; +use axum::extract::State; +use axum::routing::{get, post}; +use axum::{Json, Router}; +use axum_macros::debug_handler; +use htycommons::common::*; +use htycommons::db::*; +use htycommons::models::PushInfo; +use htycommons::web::{ + wrap_json_anyhow_err, wrap_json_ok_resp, AuthorizationHeader, HtyHostHeader, + HtySudoerTokenHeader, +}; +use std::sync::Arc; +use tower_http::trace::TraceLayer; +use tracing::{debug, error}; + +mod notifications; +mod ws_kecheng; +mod ws_repeat_kecheng; + +#[debug_handler] +async fn notify( + root: HtySudoerTokenHeader, + auth: AuthorizationHeader, + host: HtyHostHeader, + State(db_pool): State>, + push_info: Json, +) -> Json> { + debug!("notify -> starts / push_info: {:?}", push_info); + let info_push = push_info.0; + match notifications::raw_notify(info_push, root, auth, host, db_pool).await { + Ok(res) => { + debug!("notify -> success to notify!"); + wrap_json_ok_resp(res) + } + Err(e) => { + error!("notify -> failed to notify, e: {}", e); + wrap_json_anyhow_err(e) + } + } +} + +pub fn kc_rocket(db_url: &str) -> Router { + let db_state = DbState { pool: pool(db_url) }; + + let shared_db_state = Arc::new(db_state); + // + let app = Router::new() + .route( + "/api/v1/kc/create_kecheng_with_kecheng_repeat", + post(create_kecheng_with_kecheng_repeat), + ) + .route("/api/v1/kc/update_kecheng", post(update_kecheng)) + .route( + "/api/v1/kc/create_kecheng_repeat", + post(create_kecheng_repeat), + ) + .route( + "/api/v1/kc/update_kecheng_repeat", + post(update_kecheng_repeat), + ) + .route( + "/api/v1/kc/find_kechengs_by_daka_ids", + post(find_kechengs_by_daka_ids), + ) + .route( + "/api/v1/kc/find_kecheng_repeat_by_id/{id}", + get(find_kecheng_repeat_by_id), + ) + .route( + "/api/v1/kc/find_kechengs_by_hty_id", + get(find_kechengs_by_hty_id), + ) + .route( + "/api/v1/kc/find_all_non_repeatable_kechengs_within_date_range_by_hty_id", + get(find_all_non_repeatable_kechengs_within_date_range_by_hty_id), + ) + .route( + "/api/v1/kc/find_all_non_repeatable_kechengs_within_date_range", + get(find_all_non_repeatable_kechengs_within_date_range), + ) + .route( + "/api/v1/kc/find_all_repeatable_kechengs_within_date_range", + get(find_all_repeatable_kechengs_within_date_range), + ) + .route( + "/api/v1/kc/find_all_repeatable_kechengs_within_date_range_by_hty_id", + get(find_all_repeatable_kechengs_within_date_range_by_hty_id), + ) + .route("/api/v1/kc/notify", post(notify)) + .layer(TraceLayer::new_for_http()) + .with_state(shared_db_state); + + app +} diff --git a/htykc/src/main.rs b/htykc/src/main.rs new file mode 100644 index 0000000..195bddd --- /dev/null +++ b/htykc/src/main.rs @@ -0,0 +1,22 @@ +use dotenv::dotenv; +use htycommons::db::get_kc_db_url; +use htycommons::logger::logger_init; +use htycommons::web::{get_kc_port, launch_rocket}; +use htykc::kc_rocket; + +#[tokio::main] +async fn main() { + dotenv().ok(); + + logger_init(); + + let port = get_kc_port().unwrap_or_else(|e| { + eprintln!("Failed to get KC_PORT: {}", e); + std::process::exit(1); + }); + let r = launch_rocket(port, kc_rocket(&get_kc_db_url())); + let _ = r.await; + + // this is reachable only after `Shutdown::notify()` or `Ctrl+C`. + println!("Rocket: deorbit."); +} diff --git a/htykc/src/ws_repeat_kecheng.rs b/htykc/src/ws_repeat_kecheng.rs new file mode 100644 index 0000000..67bac06 --- /dev/null +++ b/htykc/src/ws_repeat_kecheng.rs @@ -0,0 +1,172 @@ +use anyhow::anyhow; +use axum::extract::State; +use axum::Json; +use htycommons::common::{HtyErr, HtyErrCode, HtyResponse}; +use htycommons::db::{extract_conn, fetch_db_conn, DbState}; +use htycommons::jwt::jwt_decode_token; +use htycommons::uuid; +use htycommons::web::{ + wrap_json_anyhow_err, wrap_json_ok_resp, AuthorizationHeader, HtyHostHeader, + HtySudoerTokenHeader, +}; +use htykc_models::models::{KechengRepeat, ReqKechengRepeat}; +use std::ops::DerefMut; +use std::sync::Arc; +use tracing::{debug, error}; + +pub async fn create_kecheng_repeat( + sudoer: HtySudoerTokenHeader, + host: HtyHostHeader, + auth: AuthorizationHeader, + State(db_pool): State>, + Json(in_kecheng_repeat): Json, +) -> Json> { + debug!("create_kecheng_repeat -> starts"); + match raw_create_kecheng_repeat(auth, sudoer, host, db_pool, &in_kecheng_repeat).await { + Ok(created_kecheng) => wrap_json_ok_resp(created_kecheng.to_req()), + Err(e) => { + error!( + "create_kecheng_repeat -> failed to create kecheng_repeat, e: {}", + e + ); + wrap_json_anyhow_err(e) + } + } +} + +async fn raw_create_kecheng_repeat( + token: AuthorizationHeader, + _sudoer: HtySudoerTokenHeader, + _host: HtyHostHeader, + db_pool: Arc, + in_kecheng_repeat: &ReqKechengRepeat, +) -> anyhow::Result { + if in_kecheng_repeat.kecheng_id.is_none() + || in_kecheng_repeat.start_from.is_none() + || in_kecheng_repeat.end_by.is_none() + || in_kecheng_repeat.repeat_cycle_days.is_none() + { + return Err(anyhow!(HtyErr { + code: HtyErrCode::WebErr, + reason: Some( + "kecheng_id or start_from or name or end_by or duration or repeat_cycle_days is none".into() + ), + })); + } + + let id_user = jwt_decode_token(&token.0)? + .hty_id + .ok_or_else(|| anyhow!("hty_id is required"))?; + debug!("raw_create_kecheng_repeat -> {:?}", id_user); + + let kecheng_repeat_id = uuid(); + + let db_kecheng_repeat = KechengRepeat { + id: kecheng_repeat_id.clone(), + kecheng_id: in_kecheng_repeat.kecheng_id.clone(), + start_from: in_kecheng_repeat.start_from.clone(), + end_by: in_kecheng_repeat.end_by.clone(), + repeat_start: in_kecheng_repeat.repeat_start.clone(), + repeat_cycle_days: in_kecheng_repeat.repeat_cycle_days.clone(), + repeat_end: in_kecheng_repeat.repeat_end.clone(), + repeat_status: in_kecheng_repeat.repeat_status.clone(), + latest_kc_created_at: in_kecheng_repeat.latest_kc_created_at.clone(), + }; + + let created_kecheng_repeat_result = KechengRepeat::create( + &db_kecheng_repeat, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + ); + + match created_kecheng_repeat_result { + Ok(res) => Ok(res), + Err(e) => Err(anyhow!(HtyErr { + code: HtyErrCode::WebErr, + reason: Some(format!("fail to create kecheng_repeat e: {}", e)), + })), + } +} + +pub async fn update_kecheng_repeat( + sudoer: HtySudoerTokenHeader, + host: HtyHostHeader, + auth: AuthorizationHeader, + State(db_pool): State>, + Json(in_kecheng_repeat): Json, +) -> Json> { + debug!("update_kecheng_repeat -> starts"); + match raw_update_kecheng_repeat(auth, sudoer, host, db_pool, &in_kecheng_repeat).await { + Ok(ok) => wrap_json_ok_resp(ok), + Err(e) => { + error!( + "update_kecheng_repeat -> failed to update kecheng, e: {}", + e + ); + wrap_json_anyhow_err(e) + } + } +} + +async fn raw_update_kecheng_repeat( + _token: AuthorizationHeader, + _sudoer: HtySudoerTokenHeader, + _host: HtyHostHeader, + db_pool: Arc, + in_kecheng_repeat: &ReqKechengRepeat, +) -> anyhow::Result { + debug!( + "raw_update_kecheng_repeat -> in_kecheng_repeat: {:?}", + in_kecheng_repeat + ); + if in_kecheng_repeat.id.is_none() { + return Err(anyhow!(HtyErr { + code: HtyErrCode::WebErr, + reason: Some("kecheng_repeat_id is none".into()), + })); + } + + let id_kecheng_repeat = in_kecheng_repeat + .id + .as_ref() + .ok_or_else(|| anyhow!("id is required"))?; + let some_db_kecheng_repeat = KechengRepeat::find_by_id( + id_kecheng_repeat, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + + debug!( + "raw_update_kecheng_repeat -> some_db_kecheng_repeat: {:?}", + some_db_kecheng_repeat + ); + + if let Some(mut c_kecheng_repeat) = some_db_kecheng_repeat { + // set new values of kecheng_repeat + + c_kecheng_repeat.kecheng_id = in_kecheng_repeat.kecheng_id.clone(); + c_kecheng_repeat.repeat_cycle_days = in_kecheng_repeat.repeat_cycle_days.clone(); + + c_kecheng_repeat.repeat_start = in_kecheng_repeat.repeat_start.clone(); + c_kecheng_repeat.repeat_end = in_kecheng_repeat.repeat_end.clone(); + + c_kecheng_repeat.repeat_status = in_kecheng_repeat.repeat_status.clone(); + + c_kecheng_repeat.start_from = in_kecheng_repeat.start_from.clone(); + c_kecheng_repeat.end_by = in_kecheng_repeat.end_by.clone(); + + debug!( + "raw_update_kecheng_repeat -> updated kecheng_repeat: {:?}", + c_kecheng_repeat + ); + + let res = KechengRepeat::update( + &c_kecheng_repeat, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + Ok(res) + } else { + Err(anyhow!(HtyErr { + code: HtyErrCode::WebErr, + reason: Some("this kecheng_repeat is not found".into()), + })) + } +} diff --git a/htykc/start.sh b/htykc/start.sh new file mode 100755 index 0000000..b1d42d7 --- /dev/null +++ b/htykc/start.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -x + +echo "----------------------------" >> htykc.log +echo "$(date)" >> htykc.log +echo "----------------------------" >> htykc.log + +nohup cargo run >> htykc.log & + diff --git a/htykc_models/Cargo.toml b/htykc_models/Cargo.toml new file mode 100644 index 0000000..a1c7911 --- /dev/null +++ b/htykc_models/Cargo.toml @@ -0,0 +1,66 @@ +[package] +name = "htykc_models" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +htycommons = { workspace = true } +htyuc_models = { workspace = true } +htyws_models = { path = "../htyws_models" } +anyhow = { workspace = true } +chrono = { workspace = true } +diesel = { workspace = true } +jsonwebtoken = { workspace = true } +log = { workspace = true } +log4rs = { workspace = true } +rand = { workspace = true } +reqwest = { workspace = true } +ring = { workspace = true } +rust-crypto = { workspace = true } +serde = { workspace = true } +serde_derive = { workspace = true } +serde_json = { workspace = true } +thiserror = { workspace = true } +time = { workspace = true } +tokio = { workspace = true } +tower-http = { workspace = true } +tracing = { workspace = true } +tracing-appender = { workspace = true } +tracing-subscriber = { workspace = true } +uuid = { workspace = true } +string-builder = { workspace = true } + +#aes = "0.8.0" +#anyhow = "1.0" +#axum = "0.5.0" +#axum-macros = "0.2.0" +#base64 = "0.13.0" +#block-modes = "0.9.1" +#chrono = { version = "0.4", features = ["serde"] } +#data-encoding = "2.3.1" +#diesel = { version = "1.4.8", features = ["postgres", "r2d2", "extras", "serde_json", "chrono"] } +#dotenv = "0.15.0" +#hex = "0.4.3" +#hex-literal = "0.3.1" +#htycommons = { path = "../htycommons" } +#jsonwebtoken = "8" +#log = "0.4.14" +#log4rs = '1.0.0' +#rand = "0.8.3" +#reqwest = { version = "0.11.1", features = ["blocking", "json"] } +#ring = "0.16.19" +#rust-crypto = "0.2.36" +#serde = { version = "1.0.2", features = ["derive"] } +#serde_derive = "1.0.2" +#serde_json = "1.0.2" +#thiserror = "1.0.29" +#time = "0.3.11" +#tokio = { version = "1.0", features = ["full"] } +#tower-http = { version = "0.3.0", features = ["trace"] } +#tracing = "0.1.31" +#tracing-appender = "0.2.0" +#tracing-subscriber = { version = "0.3", features = ["env-filter", "local-time"] } +#uuid = { version = "1.0", features = ["serde", "v4"] } diff --git a/htykc_models/diesel.toml b/htykc_models/diesel.toml new file mode 100644 index 0000000..92267c8 --- /dev/null +++ b/htykc_models/diesel.toml @@ -0,0 +1,5 @@ +# For documentation on how to configure this file, +# see diesel.rs/guides/configuring-diesel-cli + +[print_schema] +file = "src/schema.rs" diff --git a/htykc_models/migrations/.keep b/htykc_models/migrations/.keep new file mode 100644 index 0000000..e69de29 diff --git a/htykc_models/migrations/00000000000000_diesel_initial_setup/down.sql b/htykc_models/migrations/00000000000000_diesel_initial_setup/down.sql new file mode 100644 index 0000000..a9f5260 --- /dev/null +++ b/htykc_models/migrations/00000000000000_diesel_initial_setup/down.sql @@ -0,0 +1,6 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + +DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); +DROP FUNCTION IF EXISTS diesel_set_updated_at(); diff --git a/htykc_models/migrations/00000000000000_diesel_initial_setup/up.sql b/htykc_models/migrations/00000000000000_diesel_initial_setup/up.sql new file mode 100644 index 0000000..d68895b --- /dev/null +++ b/htykc_models/migrations/00000000000000_diesel_initial_setup/up.sql @@ -0,0 +1,36 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + + + + +-- Sets up a trigger for the given table to automatically set a column called +-- `updated_at` whenever the row is modified (unless `updated_at` was included +-- in the modified columns) +-- +-- # Example +-- +-- ```sql +-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); +-- +-- SELECT diesel_manage_updated_at('users'); +-- ``` +CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ +BEGIN + EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s + FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ +BEGIN + IF ( + NEW IS DISTINCT FROM OLD AND + NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at + ) THEN + NEW.updated_at := current_timestamp; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; diff --git a/htykc_models/migrations/2024-01-17-094242_init_tbls/down.sql b/htykc_models/migrations/2024-01-17-094242_init_tbls/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htykc_models/migrations/2024-01-17-094242_init_tbls/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htykc_models/migrations/2024-01-17-094242_init_tbls/up.sql b/htykc_models/migrations/2024-01-17-094242_init_tbls/up.sql new file mode 100644 index 0000000..13d1986 --- /dev/null +++ b/htykc_models/migrations/2024-01-17-094242_init_tbls/up.sql @@ -0,0 +1,73 @@ +-- Your SQL goes here + +create table kecheng +( + id varchar not null + constraint kecheng_teacher_pk + primary key, + kecheng_name varchar not null, + kecheng_status varchar not null, + kecheng_desc varchar, + start_from timestamp not null, + end_by timestamp not null, + duration integer, + root_id varchar not null + constraint kecheng_teacher_kecheng_teacher_id_fk + references kecheng, + kecheng_type varchar, + parent_id varchar not null + constraint kecheng_kecheng_id_fk + references kecheng, + created_by varchar, + created_at timestamp +); + +comment on column kecheng.duration is '课程时长(分钟)'; + +comment on column kecheng.kecheng_type is '未来可能用前端的在线上课功能'; + +comment on column kecheng.parent_id is '第一条数据的`parent_id`是自己。'; + +create unique index kecheng_teacher_id_uindex + on kecheng (id); + +create table kecheng_repeat +( + id varchar not null + primary key, + kecheng_id varchar not null + constraint kecheng_repeat_kecheng_id_fk + references kecheng, + start_from timestamp, + end_by timestamp, + repeat_start timestamp, + repeat_cycle_days integer not null, + repeat_end timestamp, + repeat_status varchar not null +); + +comment on column kecheng_repeat.start_from is '课程当天开始时间'; + +comment on column kecheng_repeat.end_by is '课程的当天结束时间'; + +comment on column kecheng_repeat.repeat_start is '不需要`repeat_end`了,因为同一组`kecheng_root_id`的数据按照`repeat_start`时间排列,后一个就是前一个的end时间。'; + +comment on column kecheng_repeat.repeat_end is '重复课程的结束时间'; + +comment on column kecheng_repeat.repeat_status is 'ON_GOING / STOPPED / PENDING / ... / STATUS优先级大于`repeat_end`'; + +create table kecheng_users +( + id varchar not null + primary key, + user_id varchar not null, + kecheng_id varchar not null + constraint kecheng_users_kecheng_id_fk + references kecheng, + kecheng_status varchar not null, + user_type varchar not null +); + +comment on column kecheng_users.user_type is '老师,学生,助教,等等'; + + diff --git a/htykc_models/migrations/2024-01-21-161522_drop_kc_users/down.sql b/htykc_models/migrations/2024-01-21-161522_drop_kc_users/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htykc_models/migrations/2024-01-21-161522_drop_kc_users/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htykc_models/migrations/2024-01-21-161522_drop_kc_users/up.sql b/htykc_models/migrations/2024-01-21-161522_drop_kc_users/up.sql new file mode 100644 index 0000000..2c75e52 --- /dev/null +++ b/htykc_models/migrations/2024-01-21-161522_drop_kc_users/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + + +drop table kecheng_users; + + diff --git a/htykc_models/migrations/2024-01-21-161707_add_students_and_teachers_fields/down.sql b/htykc_models/migrations/2024-01-21-161707_add_students_and_teachers_fields/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htykc_models/migrations/2024-01-21-161707_add_students_and_teachers_fields/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htykc_models/migrations/2024-01-21-161707_add_students_and_teachers_fields/up.sql b/htykc_models/migrations/2024-01-21-161707_add_students_and_teachers_fields/up.sql new file mode 100644 index 0000000..dbde053 --- /dev/null +++ b/htykc_models/migrations/2024-01-21-161707_add_students_and_teachers_fields/up.sql @@ -0,0 +1,10 @@ +-- Your SQL goes here + +alter table kecheng + add students jsonb; + +alter table kecheng + add teachers jsonb; + + + diff --git a/htykc_models/migrations/2024-01-21-172608_add_jihuas_and_dakas_to_kecheng/down.sql b/htykc_models/migrations/2024-01-21-172608_add_jihuas_and_dakas_to_kecheng/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htykc_models/migrations/2024-01-21-172608_add_jihuas_and_dakas_to_kecheng/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htykc_models/migrations/2024-01-21-172608_add_jihuas_and_dakas_to_kecheng/up.sql b/htykc_models/migrations/2024-01-21-172608_add_jihuas_and_dakas_to_kecheng/up.sql new file mode 100644 index 0000000..2765f09 --- /dev/null +++ b/htykc_models/migrations/2024-01-21-172608_add_jihuas_and_dakas_to_kecheng/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table kecheng + add jihuas jsonb; + +alter table kecheng + add dakas jsonb; + diff --git a/htykc_models/migrations/2024-01-28-181110_add_is_delete_to_kecheng/down.sql b/htykc_models/migrations/2024-01-28-181110_add_is_delete_to_kecheng/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htykc_models/migrations/2024-01-28-181110_add_is_delete_to_kecheng/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htykc_models/migrations/2024-01-28-181110_add_is_delete_to_kecheng/up.sql b/htykc_models/migrations/2024-01-28-181110_add_is_delete_to_kecheng/up.sql new file mode 100644 index 0000000..6e55742 --- /dev/null +++ b/htykc_models/migrations/2024-01-28-181110_add_is_delete_to_kecheng/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table kecheng + add is_delete bool default false; + diff --git a/htykc_models/migrations/2024-02-04-163858_add_is_repeat_to_kecheng/down.sql b/htykc_models/migrations/2024-02-04-163858_add_is_repeat_to_kecheng/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htykc_models/migrations/2024-02-04-163858_add_is_repeat_to_kecheng/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htykc_models/migrations/2024-02-04-163858_add_is_repeat_to_kecheng/up.sql b/htykc_models/migrations/2024-02-04-163858_add_is_repeat_to_kecheng/up.sql new file mode 100644 index 0000000..165589d --- /dev/null +++ b/htykc_models/migrations/2024-02-04-163858_add_is_repeat_to_kecheng/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + +alter table kecheng + add is_repeat bool default false; + + diff --git a/htykc_models/migrations/2024-02-05-100250_add_qumu_sections_to_kecheng/down.sql b/htykc_models/migrations/2024-02-05-100250_add_qumu_sections_to_kecheng/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htykc_models/migrations/2024-02-05-100250_add_qumu_sections_to_kecheng/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htykc_models/migrations/2024-02-05-100250_add_qumu_sections_to_kecheng/up.sql b/htykc_models/migrations/2024-02-05-100250_add_qumu_sections_to_kecheng/up.sql new file mode 100644 index 0000000..a24471a --- /dev/null +++ b/htykc_models/migrations/2024-02-05-100250_add_qumu_sections_to_kecheng/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +alter table kecheng + add qumu_sections jsonb; + diff --git a/htykc_models/migrations/2024-02-05-150135_refactor_kecheng/down.sql b/htykc_models/migrations/2024-02-05-150135_refactor_kecheng/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htykc_models/migrations/2024-02-05-150135_refactor_kecheng/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htykc_models/migrations/2024-02-05-150135_refactor_kecheng/up.sql b/htykc_models/migrations/2024-02-05-150135_refactor_kecheng/up.sql new file mode 100644 index 0000000..8a361b0 --- /dev/null +++ b/htykc_models/migrations/2024-02-05-150135_refactor_kecheng/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table kecheng_repeat + alter column kecheng_id drop not null; + +alter table kecheng_repeat + alter column repeat_status drop not null; + diff --git a/htykc_models/migrations/2024-02-05-150248_repeat_days_nullable/down.sql b/htykc_models/migrations/2024-02-05-150248_repeat_days_nullable/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htykc_models/migrations/2024-02-05-150248_repeat_days_nullable/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htykc_models/migrations/2024-02-05-150248_repeat_days_nullable/up.sql b/htykc_models/migrations/2024-02-05-150248_repeat_days_nullable/up.sql new file mode 100644 index 0000000..2c98071 --- /dev/null +++ b/htykc_models/migrations/2024-02-05-150248_repeat_days_nullable/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + +alter table kecheng_repeat + alter column repeat_cycle_days drop not null; + + diff --git a/htykc_models/migrations/2024-04-05-165754_add_is_notified_to_kc/down.sql b/htykc_models/migrations/2024-04-05-165754_add_is_notified_to_kc/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htykc_models/migrations/2024-04-05-165754_add_is_notified_to_kc/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htykc_models/migrations/2024-04-05-165754_add_is_notified_to_kc/up.sql b/htykc_models/migrations/2024-04-05-165754_add_is_notified_to_kc/up.sql new file mode 100644 index 0000000..03279dc --- /dev/null +++ b/htykc_models/migrations/2024-04-05-165754_add_is_notified_to_kc/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table kecheng + add is_notified boolean; + diff --git a/htykc_models/migrations/2024-07-26-151629_add_latest_kc_created_at_to_kecheng_repeat/down.sql b/htykc_models/migrations/2024-07-26-151629_add_latest_kc_created_at_to_kecheng_repeat/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htykc_models/migrations/2024-07-26-151629_add_latest_kc_created_at_to_kecheng_repeat/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htykc_models/migrations/2024-07-26-151629_add_latest_kc_created_at_to_kecheng_repeat/up.sql b/htykc_models/migrations/2024-07-26-151629_add_latest_kc_created_at_to_kecheng_repeat/up.sql new file mode 100644 index 0000000..ebeaec5 --- /dev/null +++ b/htykc_models/migrations/2024-07-26-151629_add_latest_kc_created_at_to_kecheng_repeat/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table kecheng_repeat + add latest_kc_created_at timestamp; + diff --git a/htykc_models/src/lib.rs b/htykc_models/src/lib.rs new file mode 100644 index 0000000..133067c --- /dev/null +++ b/htykc_models/src/lib.rs @@ -0,0 +1,9 @@ +#[macro_use] +extern crate diesel; +#[macro_use] +extern crate serde_derive; +// extern crate time; + +pub mod models; + +mod schema; diff --git a/htyproc/Cargo.toml b/htyproc/Cargo.toml new file mode 100644 index 0000000..2c9acf9 --- /dev/null +++ b/htyproc/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "htyproc" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +description = "Task processor (proc_server migration)" + +[dependencies] +htyts_models = { path = "../htyts_models" } +htycommons = { workspace = true } +dotenv = { workspace = true } +anyhow = { workspace = true } +axum = { workspace = true } +chrono = { workspace = true } +reqwest = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true } +tower-http = { workspace = true } +tracing = { workspace = true } +redis = { workspace = true } +uuid = { workspace = true } diff --git a/htyproc/src/clients.rs b/htyproc/src/clients.rs new file mode 100644 index 0000000..905a596 --- /dev/null +++ b/htyproc/src/clients.rs @@ -0,0 +1,107 @@ +use serde_json::Value; + +use htyts_models::{ReqTask, TaskStatus}; + +pub struct TsClient { + pub base: String, + pub client: reqwest::Client, +} + +impl TsClient { + pub fn new(base: &str) -> Self { + Self { + base: base.trim_end_matches('/').to_string(), + client: reqwest::Client::builder() + .timeout(crate::config::http_timeout()) + .build() + .expect("http client"), + } + } + + pub async fn one_pending_task(&self, sudo: &str) -> anyhow::Result> { + let url = format!("{}/api/v1/ts/one_pending_task", self.base); + let resp = self + .client + .get(&url) + .header("HtySudoerToken", sudo) + .send() + .await?; + if resp.status() == reqwest::StatusCode::NO_CONTENT { + return Ok(None); + } + let v: serde_json::Value = resp.json().await?; + if v.get("r").and_then(|x| x.as_bool()) != Some(true) { + return Ok(None); + } + let d = v.get("d").cloned().unwrap_or(Value::Null); + let req: ReqTask = serde_json::from_value(d)?; + Ok(Some(req)) + } + + pub async fn update_task(&self, sudo: &str, host: &str, task: &ReqTask) -> anyhow::Result<()> { + let url = format!("{}/api/v1/ts/update_task", self.base); + let resp = self + .client + .post(&url) + .header("HtySudoerToken", sudo) + .header("HtyHost", host) + .json(task) + .send() + .await?; + if !resp.status().is_success() { + anyhow::bail!("update_task failed: {}", resp.status()); + } + Ok(()) + } +} + +pub async fn ngx_combine_image( + ngx_base: &str, + sudo: &str, + host: &str, + task: &ReqTask, +) -> anyhow::Result { + let client = reqwest::Client::builder() + .timeout(crate::config::http_timeout()) + .build()?; + let url = format!("{}/api/ngx/image/combine", ngx_base.trim_end_matches('/')); + let resp = client + .post(&url) + .header("HtySudoerToken", sudo) + .header("HtyHost", host) + .json(task) + .send() + .await?; + Ok(resp.text().await?) +} + +pub async fn ngx_convert_audio( + ngx_base: &str, + sudo: &str, + host: &str, + task: &ReqTask, +) -> anyhow::Result { + let client = reqwest::Client::builder() + .timeout(crate::config::http_timeout()) + .build()?; + let url = format!("{}/api/ngx/audio/convert", ngx_base.trim_end_matches('/')); + let resp = client + .post(&url) + .header("HtySudoerToken", sudo) + .header("HtyHost", host) + .json(task) + .send() + .await?; + Ok(resp.text().await?) +} + +pub fn task_processor_label() -> &'static str { + "TASK_PROCESSOR" +} + +pub fn merge_row_status(req: &mut ReqTask, status: TaskStatus, task_id: &str) { + req.task_id = Some(task_id.to_string()); + req.task_status = Some(status); + req.updated_by = Some(task_processor_label().to_string()); + req.updated_at = Some(chrono::Utc::now().naive_utc()); +} diff --git a/htyproc/src/config.rs b/htyproc/src/config.rs new file mode 100644 index 0000000..4cc5640 --- /dev/null +++ b/htyproc/src/config.rs @@ -0,0 +1,71 @@ +use std::env; +use std::time::Duration; + +pub fn ts_url() -> anyhow::Result { + env::var("TS_URL").map_err(|_| anyhow::anyhow!("TS_URL not set")) +} + +pub fn ngx_url() -> anyhow::Result { + env::var("NGX_URL").map_err(|_| anyhow::anyhow!("NGX_URL not set")) +} + +pub fn ai_url() -> anyhow::Result { + env::var("AI_URL").map_err(|_| anyhow::anyhow!("AI_URL not set")) +} + +/// Java `HtywsProxy/mp-rest/url` — `/api/v1/ws/*`. +pub fn htyws_url() -> anyhow::Result { + env::var("HTYWS_URL").map_err(|_| anyhow::anyhow!("HTYWS_URL not set")) +} + +/// Java `HtyucProxy/mp-rest/url` — `/api/v1/uc/*`. +pub fn htyuc_url() -> anyhow::Result { + env::var("HTYUC_URL").map_err(|_| anyhow::anyhow!("HTYUC_URL not set")) +} + +pub fn ts_domain() -> anyhow::Result { + env::var("TS_DOMAIN").map_err(|_| anyhow::anyhow!("TS_DOMAIN not set")) +} + +pub fn redis_url() -> anyhow::Result { + htycommons::redis_util::get_redis_url().map_err(|e| anyhow::anyhow!(e)) +} + +pub fn proc_wait_secs() -> u64 { + env::var("WAIT_SEC") + .or_else(|_| env::var("task_server.wait_sec")) + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(5) +} + +pub fn max_processing_tasks() -> usize { + env::var("MAX_PROCESSING_TASKS") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(20) +} + +pub fn proc_port() -> u16 { + env::var("PROC_PORT") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(3004) +} + +pub fn http_timeout() -> Duration { + Duration::from_secs(120) +} + +/// Matches Java `ProcessorHelper.BIG_MAX_RETRY` (50). +pub fn ai_big_max_retry() -> u32 { + env::var("AI_BIG_MAX_RETRY") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(50) +} + +/// Sleep between AI polls; aligns with Java `Commons.waitForAWhile` using `task_server.wait_sec` / `WAIT_SEC`. +pub fn ai_poll_interval_secs() -> u64 { + proc_wait_secs() +} diff --git a/htyproc/src/lib.rs b/htyproc/src/lib.rs new file mode 100644 index 0000000..eca365f --- /dev/null +++ b/htyproc/src/lib.rs @@ -0,0 +1,33 @@ +//! Task processor (`proc_server` migration): poll TS, run handlers, `/api/v1/proc/*`. + +pub mod clients; +pub mod config; +pub mod proc_api; +pub mod processor; +pub mod redis_payload_merge; +pub mod redis_task; +pub mod tasks; + +pub use processor::ProcMachineStatus; + +use std::sync::Arc; + +use axum::routing::get; +use axum::Router; +use tower_http::trace::TraceLayer; + +use crate::processor::ProcessorRuntime; + +pub async fn proc_router() -> anyhow::Result { + let rt = ProcessorRuntime::new().await?; + Ok(proc_router_with_state(rt)) +} + +pub fn proc_router_with_state(rt: Arc) -> Router { + Router::new() + .route("/api/v1/proc/start", get(proc_api::proc_start)) + .route("/api/v1/proc/stop", get(proc_api::proc_stop)) + .route("/api/v1/proc/status", get(proc_api::proc_status)) + .with_state(rt) + .layer(TraceLayer::new_for_http()) +} diff --git a/htyproc/src/main.rs b/htyproc/src/main.rs new file mode 100644 index 0000000..c5bbdd3 --- /dev/null +++ b/htyproc/src/main.rs @@ -0,0 +1,14 @@ +use dotenv::dotenv; +use htycommons::logger::logger_init; +use htycommons::web::launch_rocket; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + dotenv().ok(); + logger_init(); + + let router = htyproc::proc_router().await?; + let port = htyproc::config::proc_port(); + launch_rocket(port, router).await?; + Ok(()) +} diff --git a/htyproc/src/proc_api.rs b/htyproc/src/proc_api.rs new file mode 100644 index 0000000..168bf47 --- /dev/null +++ b/htyproc/src/proc_api.rs @@ -0,0 +1,38 @@ +use std::sync::Arc; + +use axum::extract::State; +use axum::Json; +use htycommons::common::HtyResponse; +use htycommons::web::wrap_ok_resp; + +use crate::processor::{ProcMachineStatus, ProcessorRuntime}; + +pub async fn proc_start(State(rt): State>) -> Json> { + let should_spawn = { + let mut st = rt.machine_status.lock().expect("proc status mutex poisoned"); + if *st == ProcMachineStatus::Running { + tracing::warn!("TASK PROCESSOR ALREADY STARTED"); + false + } else { + *st = ProcMachineStatus::Running; + true + } + }; + if should_spawn { + let r = rt.clone(); + tokio::spawn(crate::processor::processor_loop(r)); + } + Json(wrap_ok_resp(ProcMachineStatus::Running.as_java_str().to_string())) +} + +pub async fn proc_stop(State(rt): State>) -> Json> { + *rt.machine_status.lock().expect("proc status mutex poisoned") = ProcMachineStatus::Abort; + Json(wrap_ok_resp(ProcMachineStatus::Abort.as_java_str().to_string())) +} + +/// `d` is a plain string (`RUNNING` / `PENDING` / …) so `htyadmin` `TaskProcessorStatus` matches Java `wrapOkResponse(Status)`. +pub async fn proc_status(State(rt): State>) -> Json> { + Json(wrap_ok_resp( + rt.proc_status().as_java_str().to_string(), + )) +} diff --git a/htyproc/src/processor.rs b/htyproc/src/processor.rs new file mode 100644 index 0000000..498cfe1 --- /dev/null +++ b/htyproc/src/processor.rs @@ -0,0 +1,189 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex}; + +use htyts_models::{ReqTask, TaskStatus, TaskType}; + +use crate::clients::{merge_row_status, ngx_combine_image, ngx_convert_audio, TsClient}; +use crate::config; +use crate::redis_task::RedisTask; +use crate::tasks; + +pub static PROCESSING: AtomicUsize = AtomicUsize::new(0); + +/// Mirrors Java `ProcessorHelper.Status`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ProcMachineStatus { + Pending, + Running, + Abort, + Error, +} + +impl ProcMachineStatus { + pub const fn as_java_str(&self) -> &'static str { + match self { + Self::Pending => "PENDING", + Self::Running => "RUNNING", + Self::Abort => "ABORT", + Self::Error => "ERROR", + } + } +} + +pub struct ProcessorRuntime { + pub machine_status: Mutex, + pub ts: TsClient, + pub redis: RedisTask, + pub sudo_token: tokio::sync::Mutex>, + pub ts_domain: String, + pub ngx_url: String, + /// Shared HTTP client for AI / htyws / htyuc (same timeout as `TsClient`). + pub http: reqwest::Client, + pub ai_url: String, + pub htyws_url: String, + pub htyuc_url: String, + /// `TS_URL` — used as `updated_by` on ref resources (Java `tsUrl`). + pub ts_url: String, +} + +impl ProcessorRuntime { + pub async fn new() -> anyhow::Result> { + let ts = TsClient::new(&config::ts_url()?); + let redis = RedisTask::connect(&config::redis_url()?).await?; + let ts_domain = config::ts_domain()?; + let ngx_url = config::ngx_url()?; + let ai_url = config::ai_url()?; + let htyws_url = config::htyws_url()?; + let htyuc_url = config::htyuc_url()?; + let ts_url = config::ts_url()?; + let http = reqwest::Client::builder() + .timeout(config::http_timeout()) + .build()?; + Ok(Arc::new(Self { + machine_status: Mutex::new(ProcMachineStatus::Pending), + ts, + redis, + sudo_token: tokio::sync::Mutex::new(std::env::var("PROC_SUDOER_TOKEN").ok()), + ts_domain, + ngx_url, + http, + ai_url, + htyws_url, + htyuc_url, + ts_url, + })) + } + + pub fn proc_status(&self) -> ProcMachineStatus { + *self.machine_status.lock().expect("proc status mutex poisoned") + } + + pub async fn sudo_token(&self) -> Option { + self.sudo_token.lock().await.clone() + } +} + +pub async fn processor_loop(rt: Arc) { + tracing::info!("processor loop started"); + loop { + { + let st = *rt.machine_status.lock().expect("proc status mutex poisoned"); + if st != ProcMachineStatus::Running { + break; + } + } + tokio::time::sleep(tokio::time::Duration::from_secs(config::proc_wait_secs())).await; + { + let st = *rt.machine_status.lock().expect("proc status mutex poisoned"); + if st != ProcMachineStatus::Running { + break; + } + } + let Some(sudo) = rt.sudo_token().await else { + tracing::warn!("PROC_SUDOER_TOKEN not set; processor idle"); + continue; + }; + let host = rt.ts_domain.clone(); + if PROCESSING.load(Ordering::SeqCst) >= config::max_processing_tasks() { + tracing::debug!("max processing tasks reached; skip poll"); + continue; + } + let pending = match rt.ts.one_pending_task(&sudo).await { + Ok(p) => p, + Err(e) => { + tracing::debug!("one_pending_task err: {e:?}"); + continue; + } + }; + let Some(mut req) = pending else { + continue; + }; + let Some(ref tid) = req.task_id else { + continue; + }; + let task_id = tid.clone(); + if let Ok(Some(p)) = rt.redis.get_payload(&task_id).await { + if let Ok(v) = serde_json::from_str(&p) { + req.payload = Some(v); + } + } + let tt = match req.task_type { + Some(t) => t, + None => continue, + }; + merge_row_status(&mut req, TaskStatus::Processing, &task_id); + if let Err(e) = rt.ts.update_task(&sudo, &host, &req).await { + tracing::error!("set processing: {e:?}"); + continue; + } + PROCESSING.fetch_add(1, Ordering::SeqCst); + let rt2 = rt.clone(); + let sudo2 = sudo.clone(); + let host2 = host.clone(); + tokio::spawn(async move { + let _ = run_one(&rt2, &sudo2, &host2, req, tt).await; + PROCESSING.fetch_sub(1, Ordering::SeqCst); + }); + } + tracing::info!("processor loop stopped"); +} + +async fn run_one( + rt: &Arc, + sudo: &str, + host: &str, + mut req: ReqTask, + tt: TaskType, +) -> anyhow::Result<()> { + let task_id = req.task_id.clone().unwrap_or_default(); + let res = match tt { + TaskType::UploadPicture => ngx_combine_image(&rt.ngx_url, sudo, host, &req) + .await + .map(|_| ()), + TaskType::ConvertAudioFile => ngx_convert_audio(&rt.ngx_url, sudo, host, &req) + .await + .map(|_| ()), + TaskType::AiScore => tasks::run_ai_score_task(rt, sudo, host, &mut req).await, + TaskType::AudioFileAiScore => { + tasks::run_audio_file_ai_score_task(rt, sudo, host, &mut req).await + } + TaskType::Watermark => tasks::run_watermark_task(rt, sudo, host, &mut req).await, + TaskType::VideoCompression => { + tasks::run_video_compression_task(rt, sudo, host, &mut req).await + } + TaskType::Noop | TaskType::TestUpyunRemove => Ok(()), + }; + match res { + Ok(()) => merge_row_status(&mut req, TaskStatus::Done, &task_id), + Err(_) => merge_row_status(&mut req, TaskStatus::Failed, &task_id), + } + if let Some(ref p) = req.payload { + let existing = rt.redis.get_payload(&task_id).await.ok().flatten(); + let merged = crate::redis_payload_merge::merge_task_payload_for_redis(existing.as_deref(), p); + if let Ok(json) = serde_json::to_string(&merged) { + let _ = rt.redis.set_payload(&task_id, &json).await; + } + } + rt.ts.update_task(sudo, host, &req).await?; + Ok(()) +} diff --git a/htyproc/src/redis_payload_merge.rs b/htyproc/src/redis_payload_merge.rs new file mode 100644 index 0000000..b09748a --- /dev/null +++ b/htyproc/src/redis_payload_merge.rs @@ -0,0 +1,124 @@ +//! Redis payload merge when proc finalizes a task — mirrors Java +//! `ProcessorHelper._finalizeTaskPayloadAndSetStatusAndGenerateTimestamp`: +//! read existing `TS_` string, put **entire** existing JSON under one timestamp key +//! and the **new** payload under a second timestamp key, then serialize the map as the new value. +//! +//! Without this, Rust proc would overwrite nested history that Java proc preserves; downstream +//! `try_parse_*` in `htyts_models` still accepts both flat and nested setups, but losing merges +//! breaks parity with production Redis shapes. + +use serde_json::{Map, Value}; + +/// Merge like Java proc `ProcessorHelper`: `{ ts1: existing_root, ts2: new_payload }`. +/// `existing_redis_json`: raw string from `GET TS_`, or `None` if missing/empty (then returns `new_payload` clone). +/// `new_payload`: in-memory task payload after pipeline (typically a flat `VideoCompressionPayload` object). +pub fn merge_task_payload_for_redis(existing_redis_json: Option<&str>, new_payload: &Value) -> Value { + merge_task_payload_for_redis_with_keys(existing_redis_json, new_payload, gen_ts_key, gen_ts_key) +} + +fn gen_ts_key() -> String { + chrono::Local::now().naive_local().to_string() +} + +/// Same merge with injectable key generators (deterministic in unit tests). +pub fn merge_task_payload_for_redis_with_keys( + existing_redis_json: Option<&str>, + new_payload: &Value, + mut key_existing: F1, + mut key_new: F2, +) -> Value +where + F1: FnMut() -> String, + F2: FnMut() -> String, +{ + let Some(raw) = existing_redis_json.map(str::trim).filter(|s| !s.is_empty()) else { + return new_payload.clone(); + }; + + let Ok(existing_val) = serde_json::from_str::(raw) else { + return new_payload.clone(); + }; + + let k1 = key_existing(); + let k2 = loop { + let k = key_new(); + if k != k1 { + break k; + } + }; + + let mut m = Map::new(); + m.insert(k1, existing_val); + m.insert(k2, new_payload.clone()); + Value::Object(m) +} + +#[cfg(test)] +mod tests { + use super::*; + use htyts_models::try_parse_video_compression_payload; + use serde_json::json; + + #[test] + fn no_existing_returns_new_unchanged() { + let newp = json!({"file_url":"u","resolution_type":"HD"}); + let out = merge_task_payload_for_redis(None, &newp); + assert_eq!(out, newp); + let out2 = merge_task_payload_for_redis(Some(""), &newp); + assert_eq!(out2, newp); + } + + #[test] + fn merge_wraps_existing_root_and_new_flat_like_java() { + let existing = json!({ + "2026-03-30T10:00:00.111":{"file_url":"old","resolution_type":"HD","hty_resource_id":"r0"} + }); + let existing_str = existing.to_string(); + let newp = json!({ + "file_url":"https://x/a.mp4", + "resolution_type":"HD", + "hty_resource_id":"r1", + "task_result":{"task_status":"SUCCESS"} + }); + let out = merge_task_payload_for_redis_with_keys( + Some(&existing_str), + &newp, + || "k_ts_existing".to_string(), + || "k_ts_new".to_string(), + ); + let obj = out.as_object().expect("top map"); + assert_eq!(obj.len(), 2); + assert_eq!(obj.get("k_ts_existing"), Some(&existing)); + assert_eq!(obj.get("k_ts_new"), Some(&newp)); + } + + #[test] + fn merged_shape_still_parses_as_video_compression_via_try_parse() { + let existing = json!({ + "2026-03-30T10:00:00.111":{ + "file_url":"https://old.mp4", + "resolution_type":"HD", + "hty_resource_id":"r0" + } + }); + let newp = json!({ + "file_url":"https://new.mp4", + "resolution_type":"HD", + "hty_resource_id":"r1", + "task_result":null + }); + let merged = merge_task_payload_for_redis_with_keys( + Some(&existing.to_string()), + &newp, + || "k_ts_a".to_string(), + || "k_ts_b".to_string(), + ); + let blob = merged.to_string(); + assert!( + blob.contains("https://old.mp4") && blob.contains("https://new.mp4"), + "merge must retain history + new flat payload" + ); + let _ = try_parse_video_compression_payload(&merged) + .expect("nested merge must remain parseable (same contract as htyts_models)"); + } +} diff --git a/htyproc/src/redis_task.rs b/htyproc/src/redis_task.rs new file mode 100644 index 0000000..59f1734 --- /dev/null +++ b/htyproc/src/redis_task.rs @@ -0,0 +1,29 @@ +use htyts_models::task_payload_redis_key; +use redis::AsyncCommands; +use redis::aio::ConnectionManager; + +#[derive(Clone)] +pub struct RedisTask { + mgr: ConnectionManager, +} + +impl RedisTask { + pub async fn connect(redis_url: &str) -> anyhow::Result { + let client = redis::Client::open(redis_url)?; + let mgr = ConnectionManager::new(client).await?; + Ok(Self { mgr }) + } + + pub async fn get_payload(&self, task_id: &str) -> anyhow::Result> { + let mut c = self.mgr.clone(); + let key = task_payload_redis_key(task_id); + Ok(c.get(&key).await?) + } + + pub async fn set_payload(&self, task_id: &str, json: &str) -> anyhow::Result<()> { + let mut c = self.mgr.clone(); + let key = task_payload_redis_key(task_id); + let _: () = c.set(&key, json).await?; + Ok(()) + } +} diff --git a/htyproc/src/tasks/mod.rs b/htyproc/src/tasks/mod.rs new file mode 100644 index 0000000..41d86b4 --- /dev/null +++ b/htyproc/src/tasks/mod.rs @@ -0,0 +1,7 @@ +//! AI / htyws / htyuc task pipelines aligned with Java `proc_server` task handlers. + +mod pipelines; + +pub use pipelines::{ + run_ai_score_task, run_audio_file_ai_score_task, run_video_compression_task, run_watermark_task, +}; diff --git a/htyproc/src/tasks/pipelines.rs b/htyproc/src/tasks/pipelines.rs new file mode 100644 index 0000000..efd1373 --- /dev/null +++ b/htyproc/src/tasks/pipelines.rs @@ -0,0 +1,587 @@ +//! Task pipelines mirroring Java `AiTaskBase`, `WatermarkTask`, `VideoCompressionTask`, `AudioFileAiScoreTask`. + +use std::sync::Arc; +use std::time::Duration; + +use anyhow::Context; +use serde_json::{json, Value}; +use tokio::time::sleep; + +use crate::clients::ngx_convert_audio; +use crate::config; +use crate::processor::ProcessorRuntime; +use htyts_models::{ + AiScorePayload, AudioFileAndAiScorePayload, CommonTaskResult, ReqTask, TaskType, + try_parse_ai_score_payload, try_parse_video_compression_payload, try_parse_watermark_payload, +}; + +const AI_STATUS_SUCCESS: &str = "SUCCESS"; +const AI_STATUS_FAILED: &str = "FAILED"; +const TASK_FROM_TS: &str = "TS"; +const TASK_FROM_AI: &str = "AI"; + +pub async fn run_ai_score_task( + rt: &Arc, + sudo: &str, + host: &str, + req: &mut ReqTask, +) -> anyhow::Result<()> { + let raw = req + .payload + .as_ref() + .ok_or_else(|| anyhow::anyhow!("missing payload"))?; + let mut payload = try_parse_ai_score_payload(raw) + .context("AiScorePayload (aligned with Java task_commons; supports legacy nested Redis shape)")?; + execute_ai_score_pipeline(rt, sudo, host, req, &mut payload).await?; + req.payload = Some(serde_json::to_value(&payload)?); + Ok(()) +} + +pub async fn run_watermark_task( + rt: &Arc, + sudo: &str, + host: &str, + req: &mut ReqTask, +) -> anyhow::Result<()> { + let raw = req + .payload + .as_ref() + .ok_or_else(|| anyhow::anyhow!("missing payload"))?; + let mut payload = try_parse_watermark_payload(raw) + .context("WatermarkPayload (aligned with Java task_commons; supports legacy nested Redis shape)")?; + let base = rt.ai_url.trim_end_matches('/'); + let url = format!("{base}/api/v1/ai/watermark"); + let resp = rt + .http + .post(&url) + .header("HtySudoerToken", sudo) + .header("HtyHost", host) + .json(&payload) + .send() + .await?; + let d = parse_hty_response(resp).await?; + let initial: CommonTaskResult = serde_json::from_value(d).context("watermark initial result")?; + let ai_task_id = initial + .task_id + .clone() + .ok_or_else(|| anyhow::anyhow!("watermark: missing ai task_id"))?; + let updated = poll_ai_until_terminal(rt, sudo, host, &ai_task_id).await?; + if updated.task_status.as_deref() == Some(AI_STATUS_FAILED) { + payload.task_result = Some(updated); + req.payload = Some(serde_json::to_value(&payload)?); + anyhow::bail!("watermark AI FAILED"); + } + payload.task_result = Some(updated); + req.payload = Some(serde_json::to_value(&payload)?); + Ok(()) +} + +pub async fn run_video_compression_task( + rt: &Arc, + sudo: &str, + host: &str, + req: &mut ReqTask, +) -> anyhow::Result<()> { + let raw = req + .payload + .as_ref() + .ok_or_else(|| anyhow::anyhow!("missing payload"))?; + let mut payload = try_parse_video_compression_payload(raw) + .context("VideoCompressionPayload (aligned with Java task_commons; supports legacy nested Redis shape)")?; + let base = rt.ai_url.trim_end_matches('/'); + let url = format!("{base}/api/v1/ai/compress"); + let resp = rt + .http + .post(&url) + .header("HtySudoerToken", sudo) + .header("HtyHost", host) + .json(&payload) + .send() + .await?; + let d = parse_hty_response(resp).await?; + let initial: CommonTaskResult = serde_json::from_value(d).context("compress initial result")?; + let ai_task_id = initial + .task_id + .clone() + .ok_or_else(|| anyhow::anyhow!("compress: missing ai task_id"))?; + payload.task_result = Some(initial); + req.payload = Some(serde_json::to_value(&payload)?); + + let compression_result = poll_ai_until_terminal(rt, sudo, host, &ai_task_id).await?; + payload.task_result = Some(compression_result.clone()); + + let status = compression_result + .task_status + .as_deref() + .unwrap_or(""); + if status == AI_STATUS_FAILED { + req.payload = Some(serde_json::to_value(&payload)?); + anyhow::bail!("video compression AI task FAILED"); + } + if status != AI_STATUS_SUCCESS { + anyhow::bail!("video compression: unexpected terminal status {status}"); + } + + let video_url = compression_result + .task_result + .as_ref() + .and_then(|m| m.get("compressed_file_url")) + .and_then(|v| v.as_str()) + .map(str::to_string) + .ok_or_else(|| anyhow::anyhow!("missing compressed_file_url"))?; + + if payload.hty_resource_id.is_none() { + req.payload = Some(serde_json::to_value(&payload)?); + return Ok(()); + } + let hty_resource_id = payload.hty_resource_id.clone().unwrap(); + + let uc_base = rt.htyuc_url.trim_end_matches('/'); + let get_url = format!("{uc_base}/api/v1/uc/find_hty_resource_by_id/{hty_resource_id}"); + let get_resp = rt + .http + .get(&get_url) + .header("HtySudoerToken", sudo) + .header("Authorization", sudo) + .header("HtyHost", host) + .send() + .await?; + let hty_d = parse_hty_response(get_resp).await?; + if hty_d.is_null() { + anyhow::bail!("find_hty_resource_by_id: empty resource for {hty_resource_id}"); + } + + let mut resource: Value = serde_json::from_value(hty_d).context("ReqHtyResource value")?; + resource["url"] = json!(video_url); + resource["updated_by"] = json!(rt.ts_url.trim_end_matches('/')); + resource["compress_processed"] = json!(false); + + let update_uc = format!("{uc_base}/api/v1/uc/update_hty_resource"); + let upd_resp = rt + .http + .post(&update_uc) + .header("HtySudoerToken", sudo) + .header("Authorization", sudo) + .header("HtyHost", host) + .json(&resource) + .send() + .await?; + let _ = parse_hty_response(upd_resp).await?; + + let ws_base = rt.htyws_url.trim_end_matches('/'); + let list_url = format!( + "{ws_base}/api/v1/ws/find_ref_resources_by_hty_resource_id/{hty_resource_id}" + ); + let list_resp = rt + .http + .get(&list_url) + .header("HtySudoerToken", sudo) + .header("Authorization", sudo) + .header("HtyHost", host) + .send() + .await?; + let list_d = parse_hty_response(list_resp).await?; + let arr = list_d + .as_array() + .cloned() + .unwrap_or_else(|| Vec::new()); + + let ts_url_short = rt.ts_url.trim_end_matches('/'); + let now = chrono::Utc::now().naive_utc(); + for raw in arr { + let mut rr: Value = raw; + rr["resource_url"] = json!(video_url); + rr["compress_processed"] = json!(false); + rr["synced_with_hty_resource"] = json!(true); + rr["updated_at"] = json!(now.to_string()); + rr["updated_by"] = json!(ts_url_short); + let upd_ref = format!("{ws_base}/api/v1/ws/update_ref_resource"); + let _ = rt + .http + .post(&upd_ref) + .header("HtySudoerToken", sudo) + .header("Authorization", sudo) + .header("HtyHost", host) + .json(&rr) + .send() + .await?; + } + + req.payload = Some(serde_json::to_value(&payload)?); + Ok(()) +} + +pub async fn run_audio_file_ai_score_task( + rt: &Arc, + sudo: &str, + host: &str, + req: &mut ReqTask, +) -> anyhow::Result<()> { + let mut combined: AudioFileAndAiScorePayload = serde_json::from_value( + req.payload + .clone() + .ok_or_else(|| anyhow::anyhow!("missing payload"))?, + ) + .context("deserialize AudioFileAndAiScorePayload")?; + + let task_id = req + .task_id + .clone() + .ok_or_else(|| anyhow::anyhow!("missing task_id"))?; + let tt = req + .task_type + .ok_or_else(|| anyhow::anyhow!("missing task_type"))?; + + let convert_req = ReqTask { + task_id: Some(task_id.clone()), + payload: Some(serde_json::to_value(&combined.audio_payload)?), + ..Default::default() + }; + let ngx_body = ngx_convert_audio(&rt.ngx_url, sudo, host, &convert_req).await?; + let upyun_path = parse_ngx_string_body(&ngx_body); + + let req_hty = build_req_hty_resource_for_audio( + &task_id, + tt, + &combined.audio_payload.media_id, + &upyun_path, + ); + let uc_base = rt.htyuc_url.trim_end_matches('/'); + let create_url = format!("{uc_base}/api/v1/uc/create_hty_resource"); + let create_resp = rt + .http + .post(&create_url) + .header("HtySudoerToken", sudo) + .header("Authorization", sudo) + .header("HtyHost", host) + .json(&req_hty) + .send() + .await?; + let created_d = parse_hty_response(create_resp).await?; + let created_id = extract_created_hty_resource_id(&created_d) + .ok_or_else(|| anyhow::anyhow!("create_hty_resource: missing id in d: {created_d:?}"))?; + + let mut req_ref = json!({ + "hty_resource_id": created_id, + "ref_id": combined.ai_score_payload.ref_id, + "resource_url": upyun_path, + "resource_type": "WX_AUDIO_FILE", + "ref_name": null, + "ref_desc": null, + "is_shifan": false, + }); + let ref_resource_id = combined.audio_payload.ref_resource_id.clone(); + if ref_resource_id.is_empty() { + req_ref["id"] = Value::Null; + } else { + req_ref["id"] = json!(ref_resource_id); + } + + let ws_base = rt.htyws_url.trim_end_matches('/'); + let upd_ref_url = format!("{ws_base}/api/v1/ws/update_ref_resource"); + let ref_resp = rt + .http + .post(&upd_ref_url) + .header("HtySudoerToken", sudo) + .header("Authorization", sudo) + .header("HtyHost", host) + .json(&req_ref) + .send() + .await?; + let ref_hty = parse_hty_response(ref_resp).await?; + let created_ref: Value = serde_json::from_value(ref_hty).context("ref resource d")?; + let compare_from_ref = created_ref + .get("resource_url") + .and_then(|v| v.as_str()) + .unwrap_or(&upyun_path); + combined.ai_score_payload.compare_url = compare_from_ref.to_string(); + + execute_ai_score_pipeline(rt, sudo, host, req, &mut combined.ai_score_payload).await?; + req.payload = Some(serde_json::to_value(&combined)?); + Ok(()) +} + +/// Core AI compare → create_score → poll → update_score flow (`AiTaskBase`). +pub(crate) async fn execute_ai_score_pipeline( + rt: &Arc, + sudo: &str, + host: &str, + req: &ReqTask, + payload: &mut AiScorePayload, +) -> anyhow::Result<()> { + let task_type = req.task_type.ok_or_else(|| anyhow::anyhow!("missing task_type"))?; + let base = rt.ai_url.trim_end_matches('/'); + let compare_url = format!("{base}/api/v1/ai/compare"); + let resp = rt + .http + .post(&compare_url) + .header("HtySudoerToken", sudo) + .header("HtyHost", host) + .json(&*payload) + .send() + .await?; + let d = parse_hty_response(resp).await?; + let compare_result: CommonTaskResult = + serde_json::from_value(d).context("compare CommonTaskResult")?; + let compare_ai_task_id = compare_result + .task_id + .clone() + .ok_or_else(|| anyhow::anyhow!("compare: missing ai task_id"))?; + + let ts_task = wrap_ts_common_task(req); + let ai_task = wrap_common_task(&compare_result, task_type.as_db_str()); + let mut req_score = build_req_score(payload, &ts_task, &ai_task, None); + let ws_base = rt.htyws_url.trim_end_matches('/'); + let create_url = format!("{ws_base}/api/v1/ws/create_score"); + let cs_resp = rt + .http + .post(&create_url) + .header("HtySudoerToken", sudo) + .header("Authorization", sudo) + .header("HtyHost", host) + .json(&req_score) + .send() + .await?; + let score_d = parse_hty_response(cs_resp).await?; + let score_id = json_value_as_string(&score_d) + .ok_or_else(|| anyhow::anyhow!("create_score: expected string id in d"))?; + if let Some(obj) = req_score.as_object_mut() { + obj.insert("id".to_string(), json!(score_id.clone())); + } + + let max_retry = config::ai_big_max_retry(); + let mut retry: u32 = 0; + loop { + sleep(Duration::from_secs(config::ai_poll_interval_secs())).await; + let updated = poll_ai_once(rt, sudo, host, &compare_ai_task_id).await?; + + let status = updated.task_status.as_deref().unwrap_or(""); + if status == AI_STATUS_FAILED { + let ts_t = wrap_ts_common_task(req); + let ai_t = wrap_common_task(&updated, task_type.as_db_str()); + let fail_score = build_req_score(payload, &ts_t, &ai_t, Some(&score_id)); + let upd_url = format!("{ws_base}/api/v1/ws/update_score"); + let _ = rt + .http + .post(&upd_url) + .header("HtySudoerToken", sudo) + .header("Authorization", sudo) + .header("HtyHost", host) + .json(&fail_score) + .send() + .await?; + anyhow::bail!("AI scoring FAILED"); + } + if status == AI_STATUS_SUCCESS { + payload.task_result = Some(updated.clone()); + let ts_t = wrap_ts_common_task(req); + let ai_t = wrap_common_task(&updated, task_type.as_db_str()); + let mut success_score = build_req_score(payload, &ts_t, &ai_t, Some(&score_id)); + attach_multi_scores(&mut success_score, payload, &updated)?; + let upd_url = format!("{ws_base}/api/v1/ws/update_score"); + let upd_resp = rt + .http + .post(&upd_url) + .header("HtySudoerToken", sudo) + .header("Authorization", sudo) + .header("HtyHost", host) + .json(&success_score) + .send() + .await?; + let _ = parse_hty_response(upd_resp).await?; + return Ok(()); + } + retry += 1; + if retry >= max_retry { + let ts_t = wrap_ts_common_task(req); + let ai_t = wrap_common_task(&updated, task_type.as_db_str()); + let timeout_score = build_req_score(payload, &ts_t, &ai_t, Some(&score_id)); + let upd_url = format!("{ws_base}/api/v1/ws/update_score"); + let _ = rt + .http + .post(&upd_url) + .header("HtySudoerToken", sudo) + .header("Authorization", sudo) + .header("HtyHost", host) + .json(&timeout_score) + .send() + .await?; + anyhow::bail!("AI task exceeded max retries"); + } + } +} + +fn attach_multi_scores( + req_score: &mut Value, + ai_score_payload: &AiScorePayload, + updated: &CommonTaskResult, +) -> anyhow::Result<()> { + let tr = updated + .task_result + .as_ref() + .ok_or_else(|| anyhow::anyhow!("missing task_result for scores"))?; + let acc = tr + .get("accuracy") + .and_then(|v| v.as_f64()) + .ok_or_else(|| anyhow::anyhow!("missing accuracy"))?; + let rec = tr + .get("recall") + .and_then(|v| v.as_f64()) + .ok_or_else(|| anyhow::anyhow!("missing recall"))?; + let vals = json!([ + { + "score_from": ai_score_payload.score_from, + "score_type": "accuracy", + "score_val": acc, + }, + { + "score_from": ai_score_payload.score_from, + "score_type": "recall", + "score_val": rec, + } + ]); + if let Some(obj) = req_score.as_object_mut() { + obj.insert("multi_scores".to_string(), json!({ "vals": vals })); + } + Ok(()) +} + +fn build_req_score( + score_payload: &AiScorePayload, + ts_task: &Value, + ai_task: &Value, + score_id: Option<&str>, +) -> Value { + let mut v = json!({ + "ref_id": score_payload.ref_id, + "ref_type": score_payload.ref_type, + "score_from": score_payload.score_from, + "meta": { "meta": { "compare_url": score_payload.compare_url } }, + "tasks": { "vals": [ ts_task, ai_task ] }, + }); + if let Some(id) = score_id { + v["id"] = json!(id); + } + v +} + +fn wrap_ts_common_task(req: &ReqTask) -> Value { + json!({ + "task_id": req.task_id, + "task_type": req.task_type.map(|t| t.as_db_str()), + "task_status": req.task_status.map(|s| s.as_db_str()), + "task_from": TASK_FROM_TS, + "duration": req.duration, + }) +} + +fn wrap_common_task(result: &CommonTaskResult, task_type: &str) -> Value { + let task_result = result + .task_result + .as_ref() + .map(|m| serde_json::Value::Object(m.iter().map(|(k, v)| (k.clone(), v.clone())).collect())); + json!({ + "task_id": result.task_id, + "task_type": task_type, + "task_status": result.task_status, + "task_from": TASK_FROM_AI, + "duration": result.duration, + "task_result": task_result, + }) +} + +async fn parse_hty_response(resp: reqwest::Response) -> anyhow::Result { + let status = resp.status(); + let v: Value = resp.json().await?; + if !status.is_success() { + anyhow::bail!("http {}: {}", status, v); + } + if v.get("r").and_then(|x| x.as_bool()) != Some(true) { + anyhow::bail!("hty r=false: {}", v); + } + Ok(v.get("d").cloned().unwrap_or(Value::Null)) +} + +async fn poll_ai_once( + rt: &Arc, + sudo: &str, + host: &str, + ai_task_id: &str, +) -> anyhow::Result { + let base = rt.ai_url.trim_end_matches('/'); + let url = format!("{base}/api/v1/ai/get_task_result?task_id={}", ai_task_id); + let resp = rt + .http + .get(&url) + .header("HtySudoerToken", sudo) + .header("HtyHost", host) + .send() + .await?; + let d = parse_hty_response(resp).await?; + serde_json::from_value(d).context("CommonTaskResult from get_task_result") +} + +async fn poll_ai_until_terminal( + rt: &Arc, + sudo: &str, + host: &str, + ai_task_id: &str, +) -> anyhow::Result { + let max_retry = config::ai_big_max_retry(); + let mut retry: u32 = 0; + loop { + if retry >= max_retry { + anyhow::bail!("poll_ai_until_terminal: exceeded max retries"); + } + sleep(Duration::from_secs(config::ai_poll_interval_secs())).await; + let updated = poll_ai_once(rt, sudo, host, ai_task_id).await?; + let status = updated.task_status.as_deref().unwrap_or(""); + if status == AI_STATUS_FAILED || status == AI_STATUS_SUCCESS { + return Ok(updated); + } + retry += 1; + } +} + +fn json_value_as_string(v: &Value) -> Option { + match v { + Value::String(s) => Some(s.clone()), + Value::Number(n) => Some(n.to_string()), + _ => None, + } +} + +fn extract_created_hty_resource_id(d: &Value) -> Option { + json_value_as_string(d) + .or_else(|| d.get("hty_resource_id").and_then(json_value_as_string)) + .or_else(|| d.get("id").and_then(json_value_as_string)) +} + +fn parse_ngx_string_body(body: &str) -> String { + let t = body.trim(); + if (t.starts_with('"') && t.ends_with('"')) || (t.starts_with('\'') && t.ends_with('\'')) { + t[1..t.len() - 1].replace("\\\"", "\"") + } else { + t.to_string() + } +} + +fn build_req_hty_resource_for_audio( + task_id: &str, + task_type: TaskType, + media_id: &str, + url: &str, +) -> Value { + json!({ + "filename": media_id, + "url": url, + "res_type": "WX_AUDIO_FILE", + "tasks": { + "vals": [{ + "task_id": task_id, + "task_type": task_type.as_db_str(), + "task_from": TASK_FROM_TS, + }] + } + }) +} diff --git a/htyproc/start.sh b/htyproc/start.sh new file mode 100755 index 0000000..72fab7b --- /dev/null +++ b/htyproc/start.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -x + +echo "----------------------------" >> htyproc.log +echo "$(date)" >> htyproc.log +echo "----------------------------" >> htyproc.log + +nohup cargo run >> htyproc.log & + diff --git a/htyts/Cargo.toml b/htyts/Cargo.toml new file mode 100644 index 0000000..0ffe7a8 --- /dev/null +++ b/htyts/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "htyts" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +description = "Task service (TS) — HTTP API and DB (Rust migration of task_server)" + +[dependencies] +htyts_models = { path = "../htyts_models" } +htycommons = { workspace = true } +anyhow = { workspace = true } +chrono = { workspace = true } +diesel = { workspace = true } +dotenv = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true } +axum = { workspace = true } +tower-http = { workspace = true } +tracing = { workspace = true } +uuid = { workspace = true } +redis = { workspace = true } +reqwest = { workspace = true } +url = { workspace = true } +once_cell = "1.20" diff --git a/htyts/src/check_auth.rs b/htyts/src/check_auth.rs new file mode 100644 index 0000000..8483654 --- /dev/null +++ b/htyts/src/check_auth.rs @@ -0,0 +1,110 @@ +//! Sudoer auth aligned with Java `CheckAuthFilter` + `RedisTokenCacheService` (`TS_SUDO_T_*`), not `HW_T_*`. + +use axum::http::StatusCode; +use axum::response::IntoResponse; +use axum::Json; +use htycommons::common::{HtyErr, HtyErrCode, HtyResponse}; +use htycommons::jwt::jwt_decode_token; +use serde::Deserialize; + +use crate::config; +use crate::redis_store::RedisTaskStore; + +#[derive(Deserialize)] +struct UcVerifyBody { + r: bool, +} + +/// Returns `401` JSON body compatible with Java `sudoErr` when rejected. +pub async fn verify_task_sudoer( + redis: &RedisTaskStore, + host: &str, + sudo_jwt: &str, +) -> Result<(), axum::response::Response> { + if !config::auth_checking() { + return Ok(()); + } + + let token = match jwt_decode_token(&sudo_jwt.to_string()) { + Ok(t) => t, + Err(e) => return Err(sudo_reject(&e.to_string())), + }; + + let tid = token.token_id.clone(); + let cache_key = htyts_models::sudoer_cache_redis_key(&tid); + if let Ok(Some(s)) = redis.get_raw(&cache_key).await { + if !s.is_empty() { + return Ok(()); + } + } + + if !config::token_verify() { + return Ok(()); + } + + let uc_base = match std::env::var("HTYUC_URL") { + Ok(u) => u, + Err(_) => return Err(sudo_reject("HTYUC_URL not set (TOKEN_VERIFY=true)")), + }; + let url = format!( + "{}/api/v1/uc/verify_jwt_token", + uc_base.trim_end_matches('/') + ); + let client = reqwest::Client::builder() + .timeout(config::http_client_timeout()) + .build() + .map_err(|e| sudo_reject(&e.to_string()))?; + let resp = client + .post(&url) + .header("HtyHost", host) + .header("Authorization", sudo_jwt) + .send() + .await + .map_err(|e| sudo_reject(&e.to_string()))?; + let status = resp.status(); + let text = resp + .text() + .await + .map_err(|e| sudo_reject(&e.to_string()))?; + let parsed: UcVerifyBody = serde_json::from_str(&text).unwrap_or(UcVerifyBody { r: false }); + if !status.is_success() || !parsed.r { + return Err(sudo_reject(&format!( + "uc verify_jwt_token failed: http={status} body={text}" + ))); + } + + let json = serde_json::to_string(&token).map_err(|e| sudo_reject(&e.to_string()))?; + let ttl = config::exp_days_for_sudo_cache().saturating_mul(24 * 3600); + redis + .set_sudoer_cache_json(&tid, &json, ttl) + .await + .map_err(|e| sudo_reject(&e.to_string()))?; + + Ok(()) +} + +pub fn missing_sudo_header() -> axum::response::Response { + let body = HtyResponse::<()> { + r: false, + d: None, + e: Some("HtySudoerTokenErr -> empty `HtySudoerToken` header".to_string()), + hty_err: Some(HtyErr { + code: HtyErrCode::AuthenticationFailed, + reason: Some("empty `HtySudoerToken` header".to_string()), + }), + }; + (StatusCode::FORBIDDEN, Json(body)).into_response() +} + +fn sudo_reject(msg: &str) -> axum::response::Response { + let body = HtyResponse::<()> { + r: false, + d: None, + e: Some(format!("HtySudoerTokenErr -> {msg}")), + hty_err: Some(HtyErr { + code: HtyErrCode::AuthenticationFailed, + reason: Some(msg.to_string()), + }), + }; + (StatusCode::UNAUTHORIZED, Json(body)).into_response() +} diff --git a/htyts/src/config.rs b/htyts/src/config.rs new file mode 100644 index 0000000..941c501 --- /dev/null +++ b/htyts/src/config.rs @@ -0,0 +1,76 @@ +use anyhow::Context; +use std::env; +use std::time::Duration; + +/// Minutes before PENDING/PROCESSING tasks are treated as zombies (Java `task_server.zombie_min`). +pub fn zombie_minutes() -> i64 { + env::var("ZOMBIE_MIN") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(10) +} + +pub fn ts_database_url() -> anyhow::Result { + env::var("TS_DATABASE_URL") + .or_else(|_| env::var("DATABASE_URL")) + .context("TS_DATABASE_URL or DATABASE_URL must be set") +} + +pub fn ts_domain() -> anyhow::Result { + env::var("TS_DOMAIN").context("TS_DOMAIN must be set") +} + +pub fn htyuc_base_url() -> anyhow::Result { + env::var("HTYUC_URL").context("HTYUC_URL must be set for kc jobs / sudo") +} + +pub fn htykc_base_url() -> anyhow::Result { + env::var("HTYKC_URL").context("HTYKC_URL must be set for kc jobs") +} + +pub fn kc_cron_default_expr() -> String { + env::var("KC_CRON_EXPR").unwrap_or_else(|_| "0 0 20 1/1 * ?".to_string()) +} + +pub fn ts_port() -> u16 { + std::env::var("TS_PORT") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(3003) +} + +pub fn http_client_timeout() -> Duration { + Duration::from_secs( + env::var("TS_HTTP_TIMEOUT_SECS") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(120), + ) +} + +/// Mirrors Java `task_server.auth_checking` (`CheckAuthFilter`). +pub fn auth_checking() -> bool { + env::var("AUTH_CHECKING") + .or_else(|_| env::var("task_server.auth_checking")) + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(true) +} + +/// Mirrors Java `task_server.token_verify` (UC `verify_jwt_token` on cache miss). +pub fn token_verify() -> bool { + env::var("TOKEN_VERIFY") + .or_else(|_| env::var("task_server.token_verify")) + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(true) +} + +/// Sudoer cache TTL in days (Java `cn.alchemystudio.taskserver.exp_days`). +pub fn exp_days_for_sudo_cache() -> u64 { + env::var("EXP_DAYS") + .or_else(|_| env::var("cn.alchemystudio.taskserver.exp_days")) + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(30) +} diff --git a/htyts/src/handlers.rs b/htyts/src/handlers.rs new file mode 100644 index 0000000..434a5ba --- /dev/null +++ b/htyts/src/handlers.rs @@ -0,0 +1,326 @@ +use std::sync::Arc; + +use axum::extract::{Path, Query, State}; +use axum::http::{HeaderMap, StatusCode}; +use axum::response::IntoResponse; +use axum::Json; +use chrono::Utc; +use htycommons::common::{HtyErr, HtyErrCode, HtyResponse}; +use htycommons::web::{wrap_anyhow_err, wrap_ok_resp, HtyHostHeader}; +use htycommons::web::HtyToken; +use htyts_models::{ReqCron, ReqTask, ReqTasksWithPage, TaskStatus}; +use serde::Deserialize; +use std::str::FromStr; + +use crate::merge::{apply_write_defaults, inject_hty_id_for_upload_picture, row_to_req_task, split_req_for_persist}; +use crate::state::TsState; + +#[derive(Deserialize)] +pub struct PageQuery { + pub page: Option, + pub page_size: Option, +} + +pub async fn hello_ts() -> &'static str { + "-=Task Server=-" +} + +pub async fn create_task( + State(state): State>, + host: HtyHostHeader, + headers: HeaderMap, + Json(req): Json, +) -> Result<(StatusCode, Json>), axum::response::Response> { + let sudo = match headers.get("HtySudoerToken").and_then(|v| v.to_str().ok()) { + Some(s) => s, + None => return Err(crate::check_auth::missing_sudo_header()), + }; + run_create_or_update(state, host, sudo, req, true).await +} + +pub async fn update_task( + State(state): State>, + host: HtyHostHeader, + headers: HeaderMap, + Json(req): Json, +) -> Result<(StatusCode, Json>), axum::response::Response> { + let sudo = match headers.get("HtySudoerToken").and_then(|v| v.to_str().ok()) { + Some(s) => s, + None => return Err(crate::check_auth::missing_sudo_header()), + }; + run_create_or_update(state, host, sudo, req, false).await +} + +async fn run_create_or_update( + state: Arc, + host: HtyHostHeader, + sudo_jwt: &str, + mut req: ReqTask, + is_create: bool, +) -> Result<(StatusCode, Json>), axum::response::Response> { + crate::check_auth::verify_task_sudoer(&state.redis, &host.0, sudo_jwt).await?; + + let now = Utc::now().naive_utc(); + apply_write_defaults(&mut req, &host.0, now); + + let hty_id = HtyToken::from_jwt(&sudo_jwt.to_string()) + .map(|t| t.hty_id) + .unwrap_or(None); + + if let Err(e) = inject_hty_id_for_upload_picture(&mut req, hty_id.clone()) { + return Err(Json(wrap_anyhow_err::(anyhow::anyhow!(e))).into_response()); + } + + let task_id = if let Some(ref id) = req.task_id { + if id.is_empty() { + uuid::Uuid::new_v4().to_string() + } else { + id.clone() + } + } else { + uuid::Uuid::new_v4().to_string() + }; + req.task_id = Some(task_id.clone()); + + let (row, redis_payload) = match split_req_for_persist(&req, task_id.clone()) { + Ok(x) => x, + Err(e) => return Err(Json(wrap_anyhow_err::(e)).into_response()), + }; + + let pool = state.db.clone(); + let row_cloned = row.clone(); + if let Err(e) = tokio::task::spawn_blocking(move || crate::upsert_task_row(&pool, &row_cloned)) + .await + .unwrap_or_else(|e| Err(anyhow::anyhow!("join: {e}"))) + { + return Err(Json(wrap_anyhow_err::(e)).into_response()); + } + + if let Some(json) = redis_payload { + if let Err(e) = state.redis.set_payload(&task_id, &json).await { + let _ = state.redis.del(&task_id).await; + return Err(Json(wrap_anyhow_err::(e)).into_response()); + } + } else if let Err(e) = state.redis.del(&task_id).await { + tracing::warn!("redis del empty payload: {e}"); + } + + let status = if is_create { + StatusCode::CREATED + } else { + StatusCode::OK + }; + Ok((status, Json(wrap_ok_resp(task_id)))) +} + +pub async fn task_status( + State(state): State>, + Path(task_id): Path, +) -> Json> { + let pool = state.db.clone(); + let tid = task_id.clone(); + let res = tokio::task::spawn_blocking(move || crate::find_task_by_id(&pool, &tid)).await; + match res { + Ok(Ok(Some(row))) => { + let ts = TaskStatus::from_str(&row.task_status).unwrap_or(TaskStatus::Pending); + Json(wrap_ok_resp(ts)) + } + Ok(Ok(None)) => Json(HtyResponse { + r: false, + d: None, + e: Some("not found".into()), + hty_err: Some(HtyErr { + code: HtyErrCode::DbErr, + reason: Some("task not found".into()), + }), + }), + Ok(Err(e)) => Json(wrap_anyhow_err(e)), + Err(e) => Json(wrap_anyhow_err(anyhow::anyhow!(e))), + } +} + +pub async fn get_task( + State(state): State>, + Path(task_id): Path, +) -> Json> { + let pool = state.db.clone(); + let tid = task_id.clone(); + let row_result = tokio::task::spawn_blocking(move || crate::find_task_by_id(&pool, &tid)).await; + let row = match row_result { + Ok(Ok(Some(r))) => r, + Ok(Ok(None)) => { + return Json(HtyResponse { + r: false, + d: None, + e: Some("not found".into()), + hty_err: Some(HtyErr { + code: HtyErrCode::NotFoundErr, + reason: Some("task not found".into()), + }), + }); + } + Ok(Err(e)) => return Json(wrap_anyhow_err(e)), + Err(e) => return Json(wrap_anyhow_err(anyhow::anyhow!(e))), + }; + + let redis_payload = state.redis.get_payload(&task_id).await.ok().flatten(); + let req = match row_to_req_task(&row, redis_payload.as_deref()) { + Ok(r) => r, + Err(e) => return Json(wrap_anyhow_err(e)), + }; + Json(wrap_ok_resp(req)) +} + +pub async fn one_pending_task( + State(state): State>, +) -> Result>, StatusCode> { + let pool = state.db.clone(); + let row = tokio::task::spawn_blocking(move || crate::one_pending_task(&pool)) + .await + .unwrap_or_else(|e| Err(anyhow::anyhow!(e))); + let row = match row { + Ok(r) => r, + Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR), + }; + let Some(row) = row else { + return Err(StatusCode::NO_CONTENT); + }; + let req = match row_to_req_task(&row, None) { + Ok(r) => r, + Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR), + }; + Ok(Json(wrap_ok_resp(req))) +} + +pub async fn one_zombie_task( + State(state): State>, +) -> Result>, StatusCode> { + let pool = state.db.clone(); + let zm = state.zombie_minutes; + let row = tokio::task::spawn_blocking(move || crate::one_zombie_task(&pool, zm)) + .await + .unwrap_or_else(|e| Err(anyhow::anyhow!(e))); + let row = match row { + Ok(r) => r, + Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR), + }; + let Some(row) = row else { + return Err(StatusCode::NO_CONTENT); + }; + let req = match row_to_req_task(&row, None) { + Ok(r) => r, + Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR), + }; + Ok(Json(wrap_ok_resp(req))) +} + +pub async fn all_tasks(State(state): State>) -> Json>> { + let pool = state.db.clone(); + let redis = state.redis.clone(); + let rows = match tokio::task::spawn_blocking(move || crate::all_tasks(&pool)).await { + Ok(Ok(r)) => r, + Ok(Err(e)) => return Json(wrap_anyhow_err(e)), + Err(e) => return Json(wrap_anyhow_err(anyhow::anyhow!(e))), + }; + let mut out = Vec::with_capacity(rows.len()); + for row in rows { + let tid = row.task_id.clone(); + let payload = redis.get_payload(&tid).await.ok().flatten(); + match row_to_req_task(&row, payload.as_deref()) { + Ok(r) => out.push(r), + Err(e) => return Json(wrap_anyhow_err(e)), + } + } + Json(wrap_ok_resp(out)) +} + +pub async fn all_tasks_with_page( + State(state): State>, + Query(q): Query, +) -> Json> { + let page: i64 = q.page.as_deref().unwrap_or("1").parse().unwrap_or(1); + let page_size: i64 = q.page_size.as_deref().unwrap_or("20").parse().unwrap_or(20); + let pool = state.db.clone(); + let redis = state.redis.clone(); + let (rows, total_page) = match tokio::task::spawn_blocking(move || { + crate::all_tasks_page(&pool, page, page_size) + }) + .await + { + Ok(Ok(r)) => r, + Ok(Err(e)) => return Json(wrap_anyhow_err(e)), + Err(e) => return Json(wrap_anyhow_err(anyhow::anyhow!(e))), + }; + let mut tasks = Vec::with_capacity(rows.len()); + for row in rows { + let tid = row.task_id.clone(); + let payload = redis.get_payload(&tid).await.ok().flatten(); + match row_to_req_task(&row, payload.as_deref()) { + Ok(r) => tasks.push(r), + Err(e) => return Json(wrap_anyhow_err(e)), + } + } + let body = ReqTasksWithPage { + tasks, + total_page: total_page as i32, + }; + Json(wrap_ok_resp(body)) +} + +pub async fn del_task( + State(state): State>, + Path(task_id): Path, +) -> Json> { + let pool = state.db.clone(); + let tid = task_id.clone(); + if let Err(e) = tokio::task::spawn_blocking(move || crate::delete_task_by_id(&pool, &tid)).await + .unwrap_or_else(|e| Err(anyhow::anyhow!(e))) + { + return Json(wrap_anyhow_err(e)); + } + if let Err(e) = state.redis.del(&task_id).await { + return Json(wrap_anyhow_err(e)); + } + Json(wrap_ok_resp(task_id)) +} + +pub async fn kc_start( + State(state): State>, +) -> Result>> { + crate::kc::scheduler::start(&state) + .await + .map_err(|e| Json(wrap_anyhow_err(e)))?; + Ok(StatusCode::OK) +} + +pub async fn kc_stop( + State(state): State>, +) -> Result>> { + crate::kc::scheduler::stop(&state) + .await + .map_err(|e| Json(wrap_anyhow_err(e)))?; + Ok(StatusCode::OK) +} + +pub async fn kc_status(State(state): State>) -> Json> { + Json(wrap_ok_resp(crate::kc::scheduler::status(&state))) +} + +pub async fn kc_curr_cron_expr(State(state): State>) -> Json> { + Json(wrap_ok_resp(crate::kc::scheduler::current_cron_expr(&state))) +} + +pub async fn kc_reset( + State(state): State>, + Json(req): Json, +) -> Result>> { + let expr = req.expr.unwrap_or_default(); + crate::kc::scheduler::reset(&state, &expr) + .await + .map_err(|e| Json(wrap_anyhow_err(e)))?; + Ok(StatusCode::OK) +} + +pub async fn kc_test(State(state): State>) -> (StatusCode, String) { + (StatusCode::OK, crate::kc::scheduler::debug_job(&state)) +} diff --git a/htyts/src/kc/mod.rs b/htyts/src/kc/mod.rs new file mode 100644 index 0000000..5f4e782 --- /dev/null +++ b/htyts/src/kc/mod.rs @@ -0,0 +1,2 @@ +pub mod scheduler; +pub mod uber; diff --git a/htyts/src/kc/scheduler.rs b/htyts/src/kc/scheduler.rs new file mode 100644 index 0000000..989cfe0 --- /dev/null +++ b/htyts/src/kc/scheduler.rs @@ -0,0 +1,92 @@ +use std::sync::atomic::{AtomicU8, Ordering}; +use std::sync::Arc; +use std::time::Duration; + +use once_cell::sync::Lazy; +use tokio::sync::Mutex; +use tokio::task::JoinHandle; + +use crate::config; +use crate::state::TsState; + +static ON: u8 = 1; +static OFF: u8 = 0; + +static KC_STATUS: AtomicU8 = AtomicU8::new(OFF); +static CRON_EXPR: Lazy> = Lazy::new(|| Mutex::new(String::new())); +static TICK_HANDLE: Lazy>>> = Lazy::new(|| Mutex::new(None)); + +fn tick_interval() -> Duration { + let secs = std::env::var("KC_INTERVAL_SECS") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(3600); + Duration::from_secs(secs) +} + +pub fn status(_state: &Arc) -> String { + if KC_STATUS.load(Ordering::SeqCst) == ON { + "ON".to_string() + } else { + "OFF".to_string() + } +} + +pub fn current_cron_expr(_state: &Arc) -> String { + CRON_EXPR + .try_lock() + .map(|g| g.clone()) + .unwrap_or_else(|_| config::kc_cron_default_expr()) +} + +pub fn debug_job(_state: &Arc) -> String { + format!( + "kc scheduler status={} cron_expr={} interval_secs={}", + status(_state), + current_cron_expr(_state), + tick_interval().as_secs() + ) +} + +pub async fn start(state: &Arc) -> anyhow::Result<()> { + let mut guard = TICK_HANDLE.lock().await; + if guard.is_some() { + return Ok(()); + } + { + let mut c = CRON_EXPR.lock().await; + if c.is_empty() { + *c = config::kc_cron_default_expr(); + } + } + let st = state.clone(); + let h = tokio::spawn(async move { + let mut interval = tokio::time::interval(tick_interval()); + interval.tick().await; + loop { + interval.tick().await; + if let Err(e) = super::uber::run_kecheng_uber(&st).await { + tracing::error!("kc uber job error: {e:?}"); + } + } + }); + *guard = Some(h); + KC_STATUS.store(ON, Ordering::SeqCst); + Ok(()) +} + +pub async fn stop(_state: &Arc) -> anyhow::Result<()> { + let mut guard = TICK_HANDLE.lock().await; + if let Some(h) = guard.take() { + h.abort(); + } + KC_STATUS.store(OFF, Ordering::SeqCst); + Ok(()) +} + +pub async fn reset(_state: &Arc, cron_expr: &str) -> anyhow::Result<()> { + stop(_state).await?; + let mut c = CRON_EXPR.lock().await; + *c = cron_expr.to_string(); + Ok(()) +} diff --git a/htyts/src/kc/uber.rs b/htyts/src/kc/uber.rs new file mode 100644 index 0000000..c4cd9d6 --- /dev/null +++ b/htyts/src/kc/uber.rs @@ -0,0 +1,62 @@ +//! Course notification tick: calls HTYKC similarly to Java `UberTask` (non-repeatable path stub). +//! +//! Uses `TS_SUDOER_TOKEN` when set; otherwise skips. Full cert-login parity is not implemented here. + +use std::sync::Arc; + +use chrono::Utc; + +use crate::config; +use crate::state::TsState; + +fn sudo_token() -> Option { + std::env::var("TS_SUDOER_TOKEN").ok().filter(|s| !s.is_empty()) +} + +pub async fn run_kecheng_uber(state: &Arc) -> anyhow::Result<()> { + let _ = state; + let Some(token) = sudo_token() else { + tracing::warn!("kc uber: TS_SUDOER_TOKEN not set; skipping kecheng notification tick"); + return Ok(()); + }; + let ts_domain = config::ts_domain()?; + let htykc = config::htykc_base_url()?; + let client = reqwest::Client::builder() + .timeout(config::http_client_timeout()) + .build()?; + + let now = Utc::now().naive_local(); + let end = now + chrono::Duration::hours(48); + let fmt = "%Y-%m-%d %H:%M:%S"; + let start_from = now.format(fmt).to_string(); + let end_by = end.format(fmt).to_string(); + + let query = url::form_urlencoded::Serializer::new(String::new()) + .append_pair("start_from", &start_from) + .append_pair("end_by", &end_by) + .finish(); + let url = format!( + "{}/api/v1/kc/find_all_non_repeatable_kechengs_within_date_range?{query}", + htykc.trim_end_matches('/') + ); + + let resp = client + .get(&url) + .header("HtySudoerToken", &token) + .header("Authorization", &token) + .header("HtyHost", &ts_domain) + .send() + .await?; + + let status = resp.status(); + let text = resp.text().await.unwrap_or_default(); + tracing::info!( + "kc uber: non_repeatable kechengs status={status} body_len={}", + text.len() + ); + if let Ok(v) = serde_json::from_str::(&text) { + tracing::debug!("kc uber response: {v}"); + } + + Ok(()) +} diff --git a/htyts/src/lib.rs b/htyts/src/lib.rs new file mode 100644 index 0000000..3ef98b0 --- /dev/null +++ b/htyts/src/lib.rs @@ -0,0 +1,73 @@ +//! Task service (`task_server` migration): `/api/v1/ts` HTTP API, PostgreSQL, Redis, kc scheduler. + +pub mod check_auth; +pub mod config; +pub mod handlers; +pub mod kc; +pub mod merge; +pub mod redis_store; +pub mod state; + +use std::sync::Arc; + +use axum::routing::{get, post}; +use axum::Router; +use htycommons::db::pool; +use tower_http::trace::TraceLayer; + +use crate::redis_store::RedisTaskStore; +use crate::state::TsState; + +pub use htyts_models::schema::dbtask; +pub use htyts_models::{ + all_tasks, all_tasks_page, delete_task_by_id, find_task_by_id, one_pending_task, one_zombie_task, + upsert_task_row, DbTaskRow, +}; + +pub async fn ts_router(db_url: &str, redis_url: &str, zombie_minutes: i64) -> anyhow::Result { + let pg = pool(db_url); + let redis = RedisTaskStore::connect(redis_url).await?; + let st = Arc::new(TsState { + db: Arc::new(pg), + redis, + zombie_minutes, + }); + Ok(ts_router_with_state(st)) +} + +pub fn ts_router_with_state(state: Arc) -> Router { + Router::new() + .route("/api/v1/ts", get(handlers::hello_ts)) + .route("/api/v1/ts/create_task", post(handlers::create_task)) + .route("/api/v1/ts/update_task", post(handlers::update_task)) + .route( + "/api/v1/ts/task_status/{task_id}", + get(handlers::task_status), + ) + .route("/api/v1/ts/task/{task_id}", get(handlers::get_task)) + .route( + "/api/v1/ts/one_pending_task", + get(handlers::one_pending_task), + ) + .route( + "/api/v1/ts/one_zombie_task", + get(handlers::one_zombie_task), + ) + .route("/api/v1/ts/all_tasks", get(handlers::all_tasks)) + .route( + "/api/v1/ts/all_tasks_with_page", + get(handlers::all_tasks_with_page), + ) + .route("/api/v1/ts/del_task/{task_id}", get(handlers::del_task)) + .route("/api/v1/ts/kc/start", get(handlers::kc_start)) + .route("/api/v1/ts/kc/stop", get(handlers::kc_stop)) + .route("/api/v1/ts/kc/status", get(handlers::kc_status)) + .route( + "/api/v1/ts/kc/curr_cron_expr", + get(handlers::kc_curr_cron_expr), + ) + .route("/api/v1/ts/kc/reset", post(handlers::kc_reset)) + .route("/api/v1/ts/kc/test", get(handlers::kc_test)) + .layer(TraceLayer::new_for_http()) + .with_state(state) +} diff --git a/htyts/src/main.rs b/htyts/src/main.rs new file mode 100644 index 0000000..5a196fd --- /dev/null +++ b/htyts/src/main.rs @@ -0,0 +1,25 @@ +use dotenv::dotenv; +use htycommons::logger::logger_init; +use htycommons::web::launch_rocket; +use std::sync::Arc; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + dotenv().ok(); + logger_init(); + + let db_url = htyts::config::ts_database_url()?; + let redis_url = htycommons::redis_util::get_redis_url()?; + let zombie = htyts::config::zombie_minutes(); + let pg = htycommons::db::pool(&db_url); + let redis = htyts::redis_store::RedisTaskStore::connect(&redis_url).await?; + let st = Arc::new(htyts::state::TsState { + db: Arc::new(pg), + redis, + zombie_minutes: zombie, + }); + let router = htyts::ts_router_with_state(st); + let port = htyts::config::ts_port(); + launch_rocket(port, router).await?; + Ok(()) +} diff --git a/htyts/src/merge.rs b/htyts/src/merge.rs new file mode 100644 index 0000000..d5dd209 --- /dev/null +++ b/htyts/src/merge.rs @@ -0,0 +1,119 @@ +use chrono::NaiveDateTime; +use std::str::FromStr; +use htyts_models::{ReqTask, TaskStatus, TaskType}; +use serde_json::{json, Value}; + +use crate::DbTaskRow; + +pub fn apply_write_defaults(req: &mut ReqTask, host: &str, now: NaiveDateTime) { + if req.task_status.is_none() { + req.task_status = Some(TaskStatus::Pending); + } + if req.created_by.as_ref().map(|s| s.is_empty()).unwrap_or(true) { + req.created_by = Some(host.to_string()); + } + if req.created_at.is_none() { + req.created_at = Some(now); + } + req.updated_by = Some(host.to_string()); + req.updated_at = Some(now); +} + +/// Build DB row + optional Redis JSON string from request (after [`apply_write_defaults`]). +pub fn split_req_for_persist(req: &ReqTask, task_id: String) -> anyhow::Result<(DbTaskRow, Option)> { + let tt = req + .task_type + .ok_or_else(|| anyhow::anyhow!("task_type is required"))?; + let ts = req + .task_status + .ok_or_else(|| anyhow::anyhow!("task_status is required"))?; + let created_by = req + .created_by + .clone() + .ok_or_else(|| anyhow::anyhow!("created_by is required"))?; + let created_at = req + .created_at + .ok_or_else(|| anyhow::anyhow!("created_at is required"))?; + let updated_by = req + .updated_by + .clone() + .ok_or_else(|| anyhow::anyhow!("updated_by is required"))?; + let updated_at = req + .updated_at + .ok_or_else(|| anyhow::anyhow!("updated_at is required"))?; + + let row = DbTaskRow { + task_id, + hty_id: req.hty_id.clone(), + task_type: tt.as_db_str().to_string(), + task_status: ts.as_db_str().to_string(), + duration: req.duration, + created_by, + created_at, + updated_by, + updated_at, + meta: req.meta.clone(), + }; + + let redis_str = if let Some(ref p) = req.payload { + Some(serde_json::to_string(p)?) + } else { + None + }; + + Ok((row, redis_str)) +} + +/// Injects `hty_id` into JSON object payload for `UPLOAD_PICTURE` (Java `TaskService.createOrUpdateTask`). +pub fn inject_hty_id_for_upload_picture( + req: &mut ReqTask, + hty_id: Option, +) -> anyhow::Result<()> { + if req.task_type != Some(TaskType::UploadPicture) { + return Ok(()); + } + let Some(hty_id) = hty_id else { + return Ok(()); + }; + let Some(ref mut payload) = req.payload else { + return Ok(()); + }; + match payload { + Value::Object(map) => { + map.insert("hty_id".to_string(), json!(hty_id)); + } + _ => { + *payload = json!({ "hty_id": hty_id }); + } + } + Ok(()) +} + +pub fn row_to_req_task(row: &DbTaskRow, payload_json: Option<&str>) -> anyhow::Result { + let task_type = + TaskType::from_str(&row.task_type).map_err(|e| anyhow::anyhow!("task_type: {e}"))?; + let task_status = + TaskStatus::from_str(&row.task_status).map_err(|e| anyhow::anyhow!("task_status: {e}"))?; + let payload = if let Some(s) = payload_json { + if s.is_empty() { + None + } else { + Some(serde_json::from_str(s)?) + } + } else { + None + }; + Ok(ReqTask { + task_id: Some(row.task_id.clone()), + hty_id: row.hty_id.clone(), + created_by: Some(row.created_by.clone()), + created_at: Some(row.created_at), + updated_by: Some(row.updated_by.clone()), + updated_at: Some(row.updated_at), + task_type: Some(task_type), + task_status: Some(task_status), + duration: row.duration, + meta: row.meta.clone(), + payload, + }) +} diff --git a/htyts/src/redis_store.rs b/htyts/src/redis_store.rs new file mode 100644 index 0000000..f8c6c87 --- /dev/null +++ b/htyts/src/redis_store.rs @@ -0,0 +1,59 @@ +use anyhow::Context; +use htyts_models::{sudoer_cache_redis_key, task_payload_redis_key}; +use redis::AsyncCommands; +use redis::aio::ConnectionManager; + +#[derive(Clone)] +pub struct RedisTaskStore { + mgr: ConnectionManager, +} + +impl RedisTaskStore { + pub async fn connect(redis_url: &str) -> anyhow::Result { + let client = redis::Client::open(redis_url).context("redis client")?; + let mgr = ConnectionManager::new(client) + .await + .context("redis connection manager")?; + Ok(Self { mgr }) + } + + pub async fn set_payload(&self, task_id: &str, json: &str) -> anyhow::Result<()> { + let mut c = self.mgr.clone(); + let key = task_payload_redis_key(task_id); + let _: () = c.set(&key, json).await?; + Ok(()) + } + + pub async fn get_payload(&self, task_id: &str) -> anyhow::Result> { + let mut c = self.mgr.clone(); + let key = task_payload_redis_key(task_id); + let v: Option = c.get(&key).await?; + Ok(v) + } + + pub async fn del(&self, task_id: &str) -> anyhow::Result<()> { + let mut c = self.mgr.clone(); + let key = task_payload_redis_key(task_id); + let _: i64 = c.del(&key).await?; + Ok(()) + } + + pub async fn get_raw(&self, key: &str) -> anyhow::Result> { + let mut c = self.mgr.clone(); + let v: Option = c.get(key).await?; + Ok(v) + } + + /// After UC `verify_jwt_token` succeeds, mirror Java `setRequestSudoerToken` (TTL in seconds). + pub async fn set_sudoer_cache_json( + &self, + token_id: &str, + json: &str, + ttl_secs: u64, + ) -> anyhow::Result<()> { + let mut c = self.mgr.clone(); + let key = sudoer_cache_redis_key(token_id); + let _: () = c.set_ex(&key, json, ttl_secs).await?; + Ok(()) + } +} diff --git a/htyts/src/state.rs b/htyts/src/state.rs new file mode 100644 index 0000000..c7a6931 --- /dev/null +++ b/htyts/src/state.rs @@ -0,0 +1,12 @@ +use std::sync::Arc; + +use htycommons::db::PgPool; + +use crate::redis_store::RedisTaskStore; + +#[derive(Clone)] +pub struct TsState { + pub db: Arc, + pub redis: RedisTaskStore, + pub zombie_minutes: i64, +} diff --git a/htyts/start.sh b/htyts/start.sh new file mode 100755 index 0000000..105a257 --- /dev/null +++ b/htyts/start.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -x + +echo "----------------------------" >> htyts.log +echo "$(date)" >> htyts.log +echo "----------------------------" >> htyts.log + +nohup cargo run >> htyts.log & + diff --git a/htyts/tests/ts_e2e_authcore_http.rs b/htyts/tests/ts_e2e_authcore_http.rs new file mode 100644 index 0000000..ca6d78c --- /dev/null +++ b/htyts/tests/ts_e2e_authcore_http.rs @@ -0,0 +1,176 @@ +//! HTYTS + AuthCore HTYUC 联调:需已启动 `htyuc`(`HTYUC_URL`),且 `JWT_KEY` 与 UC 一致。 +//! UC `verify_jwt` 要求 JWT 已在 UC Redis 中(与 `token_id` 一致),故通过 `login_with_password` 取令牌(数据见 AuthCore `htyuc/tests/fixtures/init_test_data.sql`)。 +//! 在 CI 中由 `.github/workflows/htyts-authcore-weekly.yml` 构建并启动 UC 后执行;不在默认 PR workflow 中跑。 + +use std::sync::Arc; +use std::time::Duration; + +use axum::serve; +use htycommons::db::pool; +use htyts::redis_store::RedisTaskStore; +use htyts::state::TsState; +use htyts::ts_router_with_state; +use serde_json::{json, Value}; +use tokio::net::TcpListener; + +/// 与 AuthCore `rust.yml` e2e 及 UC 进程保持一致,便于 `verify_jwt_token` 校验同一密钥。 +const JWT_KEY_AUTHCORE_E2E: &str = "test_jwt_key_for_testing_only_1234567890"; + +/// 与 `init_test_data.sql` 中 `hty_apps.domain` 一致,登录与后续请求的 `HtyHost` 须相同。 +const UC_E2E_HTY_HOST: &str = "root"; +/// 具备 `SYS_CAN_SUDO` 的测试用户(fixture)。 +const UC_E2E_SUDO_USER: &str = "sudouser"; +const UC_E2E_SUDO_PASS: &str = "sudopass"; + +fn ensure_authcore_joint_env() { + std::env::var("HTYUC_URL").expect( + "HTYUC_URL must point to running HTYUC (e.g. http://127.0.0.1:18080). \ + See huiwing/scripts/ci-authcore-weekly.sh or the weekly GitHub workflow.", + ); + std::env::set_var("JWT_KEY", JWT_KEY_AUTHCORE_E2E); + std::env::set_var("TOKEN_VERIFY", "true"); + std::env::set_var("AUTH_CHECKING", "true"); + if std::env::var("REDIS_HOST").is_err() { + std::env::set_var("REDIS_HOST", "127.0.0.1"); + } + if std::env::var("REDIS_PORT").is_err() { + std::env::set_var("REDIS_PORT", "6379"); + } + if std::env::var("POOL_SIZE").is_err() { + std::env::set_var("POOL_SIZE", "8"); + } + if std::env::var("TS_DOMAIN").is_err() { + std::env::set_var("TS_DOMAIN", "e2e.test.local"); + } +} + +fn redis_url() -> String { + let host = std::env::var("REDIS_HOST").expect("REDIS_HOST"); + let port = std::env::var("REDIS_PORT").expect("REDIS_PORT"); + format!("redis://{host}:{port}") +} + +async fn fetch_sudoer_jwt_via_uc_login() -> String { + ensure_authcore_joint_env(); + let uc_base = std::env::var("HTYUC_URL").expect("HTYUC_URL"); + let client = reqwest::Client::builder() + .timeout(Duration::from_secs(120)) + .build() + .expect("reqwest client"); + let login_body = json!({ + "username": UC_E2E_SUDO_USER, + "password": UC_E2E_SUDO_PASS, + }); + let resp = client + .post(format!( + "{}/api/v1/uc/login_with_password", + uc_base.trim_end_matches('/') + )) + .header("HtyHost", UC_E2E_HTY_HOST) + .json(&login_body) + .send() + .await + .expect("login_with_password"); + let status = resp.status(); + let text = resp.text().await.unwrap_or_default(); + assert!( + status.is_success(), + "login_with_password http={status} body={text}" + ); + let v: Value = serde_json::from_str(&text).expect("login json"); + assert!( + v["r"].as_bool().unwrap_or(false), + "login r=false body={text}" + ); + v["d"] + .as_str() + .expect("login token in d") + .to_string() +} + +async fn spawn_ts_server() -> String { + ensure_authcore_joint_env(); + let db_url = std::env::var("TS_DATABASE_URL").expect("TS_DATABASE_URL"); + let pg = pool(&db_url); + let redis = RedisTaskStore::connect(&redis_url()) + .await + .expect("RedisTaskStore::connect"); + let st = Arc::new(TsState { + db: Arc::new(pg), + redis, + zombie_minutes: std::env::var("ZOMBIE_MIN") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(10), + }); + let app = ts_router_with_state(st); + let listener = TcpListener::bind("127.0.0.1:0") + .await + .expect("bind ephemeral port"); + let addr = listener.local_addr().expect("local_addr"); + tokio::spawn(async move { + if let Err(e) = serve(listener, app).await { + eprintln!("htyts authcore e2e server error: {e}"); + } + }); + tokio::time::sleep(Duration::from_millis(200)).await; + format!("http://127.0.0.1:{}", addr.port()) +} + +/// 首次 `create_task` 在缓存未命中时应走 UC `POST /api/v1/uc/verify_jwt_token`(与 `check_auth` 一致)。 +/// 默认 `cargo test` 跳过;周更 CI 与本地联调使用 `cargo test -p htyts --test ts_e2e_authcore_http -- --ignored`。 +#[tokio::test] +#[ignore = "needs HTYUC_URL + running HTYUC (AuthCore); see scripts/ci-authcore-weekly.sh"] +async fn create_task_with_uc_verify_jwt_token() { + let base = spawn_ts_server().await; + let sudo = fetch_sudoer_jwt_via_uc_login().await; + let client = reqwest::Client::builder() + .timeout(Duration::from_secs(120)) + .build() + .expect("reqwest client"); + + let create_body = json!({ + "task_type": "NOOP", + "task_status": "PENDING", + "payload": { "e2e_authcore": "note" } + }); + let create = client + .post(format!("{base}/api/v1/ts/create_task")) + .header("HtyHost", UC_E2E_HTY_HOST) + .header("HtySudoerToken", &sudo) + .json(&create_body) + .send() + .await + .expect("create_task"); + let status = create.status(); + let body_text = create.text().await.unwrap_or_default(); + assert_eq!( + status, + reqwest::StatusCode::CREATED, + "create_task (cache miss → UC verify) body={}", + body_text + ); + let created: Value = serde_json::from_str(&body_text).expect("create json"); + assert_eq!(created["r"], true); + + let create2 = client + .post(format!("{base}/api/v1/ts/create_task")) + .header("HtyHost", UC_E2E_HTY_HOST) + .header("HtySudoerToken", &sudo) + .json(&json!({ + "task_type": "NOOP", + "task_status": "PENDING", + "payload": { "e2e_authcore": "second_same_sudo_cache" } + })) + .send() + .await + .expect("create_task 2"); + let status2 = create2.status(); + let body2 = create2.text().await.unwrap_or_default(); + assert_eq!( + status2, + reqwest::StatusCode::CREATED, + "second create (Redis sudo cache hit) body={}", + body2 + ); +} diff --git a/htyts/tests/ts_e2e_http.rs b/htyts/tests/ts_e2e_http.rs new file mode 100644 index 0000000..9979abe --- /dev/null +++ b/htyts/tests/ts_e2e_http.rs @@ -0,0 +1,181 @@ +//! HTTP e2e against a real Postgres + Redis (see `huiwing/docker-compose.ts-e2e.yml`). +//! Run: `huiwing/scripts/run-ts-e2e.sh` (starts compose, truncates data, runs this test). + +use std::sync::Arc; +use std::time::Duration; + +use axum::serve; +use chrono::Utc; +use htycommons::db::pool; +use htycommons::jwt::jwt_encode_token; +use htycommons::web::HtyToken; +use htyts::redis_store::RedisTaskStore; +use htyts::state::TsState; +use htyts::ts_router_with_state; +use serde_json::{json, Value}; +use tokio::net::TcpListener; + +fn ensure_e2e_env_defaults() { + if std::env::var("JWT_KEY").is_err() { + std::env::set_var("JWT_KEY", "e2e-jwt-secret-key-minimum-32-chars!!"); + } + if std::env::var("REDIS_HOST").is_err() { + std::env::set_var("REDIS_HOST", "127.0.0.1"); + } + if std::env::var("REDIS_PORT").is_err() { + std::env::set_var("REDIS_PORT", "6390"); + } + if std::env::var("POOL_SIZE").is_err() { + std::env::set_var("POOL_SIZE", "8"); + } + // Avoid calling real HTYUC in e2e; matches optional UC verify when unset in dev. + if std::env::var("TOKEN_VERIFY").is_err() { + std::env::set_var("TOKEN_VERIFY", "false"); + } +} + +fn redis_url() -> String { + let host = std::env::var("REDIS_HOST").expect("REDIS_HOST"); + let port = std::env::var("REDIS_PORT").expect("REDIS_PORT"); + format!("redis://{host}:{port}") +} + +/// Mint sudoer JWT (`TOKEN_VERIFY=false` in e2e so `TS_SUDO_T_*` / UC are not required). +fn mint_sudo_jwt() -> String { + ensure_e2e_env_defaults(); + let inner = HtyToken { + token_id: "e2e_sudo".to_string(), + hty_id: Some("hty-e2e-1".to_string()), + app_id: None, + ts: Utc::now().naive_utc(), + roles: None, + tags: None, + }; + jwt_encode_token(inner).expect("jwt_encode_token") +} + +async fn spawn_ts_server() -> String { + ensure_e2e_env_defaults(); + let db_url = std::env::var("TS_DATABASE_URL").expect( + "TS_DATABASE_URL must be set (run `huiwing/scripts/run-ts-e2e.sh` or docker-compose.ts-e2e)", + ); + let pg = pool(&db_url); + let redis = RedisTaskStore::connect(&redis_url()) + .await + .expect("RedisTaskStore::connect"); + let st = Arc::new(TsState { + db: Arc::new(pg), + redis, + zombie_minutes: std::env::var("ZOMBIE_MIN") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(10), + }); + let app = ts_router_with_state(st); + let listener = TcpListener::bind("127.0.0.1:0") + .await + .expect("bind ephemeral port"); + let addr = listener.local_addr().expect("local_addr"); + tokio::spawn(async move { + if let Err(e) = serve(listener, app).await { + eprintln!("htyts e2e server error: {e}"); + } + }); + tokio::time::sleep(Duration::from_millis(150)).await; + format!("http://127.0.0.1:{}", addr.port()) +} + +#[tokio::test] +async fn htyts_create_get_pending_update_delete_flow() { + let sudo = mint_sudo_jwt(); + let base = spawn_ts_server().await; + let client = reqwest::Client::builder() + .timeout(Duration::from_secs(60)) + .build() + .expect("reqwest client"); + + let hello = client + .get(format!("{base}/api/v1/ts")) + .send() + .await + .expect("hello"); + assert_eq!(hello.status(), reqwest::StatusCode::OK); + assert_eq!(hello.text().await.expect("body"), "-=Task Server=-"); + + let create_body = json!({ + "task_type": "NOOP", + "task_status": "PENDING", + "payload": { "e2e": "note" } + }); + let create = client + .post(format!("{base}/api/v1/ts/create_task")) + .header("HtyHost", "e2e.test.local") + .header("HtySudoerToken", &sudo) + .json(&create_body) + .send() + .await + .expect("create_task"); + assert_eq!(create.status(), reqwest::StatusCode::CREATED); + let created: Value = create.json().await.expect("create json"); + assert_eq!(created["r"], true); + let task_id = created["d"] + .as_str() + .expect("task id string") + .to_string(); + + let get = client + .get(format!("{base}/api/v1/ts/task/{task_id}")) + .send() + .await + .expect("get task"); + assert_eq!(get.status(), reqwest::StatusCode::OK); + let got: Value = get.json().await.expect("get json"); + assert_eq!(got["r"], true); + let task = &got["d"]; + assert_eq!(task["task_type"], "NOOP"); + assert_eq!(task["task_status"], "PENDING"); + assert!(task["payload"]["e2e"].is_string()); + + let st = client + .get(format!("{base}/api/v1/ts/task_status/{task_id}")) + .send() + .await + .expect("task_status"); + assert_eq!(st.status(), reqwest::StatusCode::OK); + let stj: Value = st.json().await.expect("status json"); + assert_eq!(stj["d"], "PENDING"); + + let pend = client + .get(format!("{base}/api/v1/ts/one_pending_task")) + .send() + .await + .expect("one_pending"); + assert_eq!(pend.status(), reqwest::StatusCode::OK); + let pj: Value = pend.json().await.expect("pending json"); + assert_eq!(pj["r"], true); + assert_eq!(pj["d"]["task_id"], task_id); + + let update_body = json!({ + "task_id": task_id, + "task_type": "NOOP", + "task_status": "DONE" + }); + let upd = client + .post(format!("{base}/api/v1/ts/update_task")) + .header("HtyHost", "e2e.test.local") + .header("HtySudoerToken", &sudo) + .json(&update_body) + .send() + .await + .expect("update_task"); + assert_eq!(upd.status(), reqwest::StatusCode::OK); + + let del = client + .get(format!("{base}/api/v1/ts/del_task/{task_id}")) + .send() + .await + .expect("del_task"); + assert_eq!(del.status(), reqwest::StatusCode::OK); + let dj: Value = del.json().await.expect("del json"); + assert_eq!(dj["r"], true); +} diff --git a/htyts_models/Cargo.toml b/htyts_models/Cargo.toml new file mode 100644 index 0000000..4c1d5d1 --- /dev/null +++ b/htyts_models/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "htyts_models" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +description = "Task service (TS) and processor JSON/DTO types aligned with Java task_commons" + +[dependencies] +anyhow = { workspace = true } +chrono = { workspace = true } +diesel = { workspace = true } +htycommons = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } diff --git a/htyts_models/diesel.toml b/htyts_models/diesel.toml new file mode 100644 index 0000000..925e2ed --- /dev/null +++ b/htyts_models/diesel.toml @@ -0,0 +1,5 @@ +# For documentation on how to configure this file, +# see https://diesel.rs/guides/configuring-diesel-cli + +[print_schema] +file = "src/schema.rs" diff --git a/htyts_models/migrations/2026-03-22-120000_create_dbtask/down.sql b/htyts_models/migrations/2026-03-22-120000_create_dbtask/down.sql new file mode 100644 index 0000000..1458eac --- /dev/null +++ b/htyts_models/migrations/2026-03-22-120000_create_dbtask/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS dbtask; diff --git a/htyts_models/migrations/2026-03-22-120000_create_dbtask/up.sql b/htyts_models/migrations/2026-03-22-120000_create_dbtask/up.sql new file mode 100644 index 0000000..8176b37 --- /dev/null +++ b/htyts_models/migrations/2026-03-22-120000_create_dbtask/up.sql @@ -0,0 +1,14 @@ +-- Hibernate `DbTask` → table `dbtask` (Java task_commons / task_server). + +CREATE TABLE dbtask ( + task_id TEXT PRIMARY KEY, + hty_id TEXT, + task_type TEXT NOT NULL, + task_status TEXT NOT NULL, + duration DOUBLE PRECISION, + created_by TEXT NOT NULL, + created_at TIMESTAMP NOT NULL, + updated_by TEXT NOT NULL, + updated_at TIMESTAMP NOT NULL, + meta TEXT +); diff --git a/htyts_models/src/common_task_result.rs b/htyts_models/src/common_task_result.rs new file mode 100644 index 0000000..7b17565 --- /dev/null +++ b/htyts_models/src/common_task_result.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value as JsonValue; +use std::collections::HashMap; + +/// Mirrors [CommonTaskResult](task_commons/src/main/java/cn/alchemystudio/taskcommons/web/CommonTaskResult.java). +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct CommonTaskResult { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub duration: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub task_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub task_result: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub task_status: Option, +} diff --git a/htyts_models/src/lib.rs b/htyts_models/src/lib.rs new file mode 100644 index 0000000..4ccdad9 --- /dev/null +++ b/htyts_models/src/lib.rs @@ -0,0 +1,34 @@ +//! Types aligned with Java `task_commons` for `/api/v1/ts` and proc handlers. +//! +//! JSON shape follows Quarkus/Jackson/JSON-B usage: optional fields are omitted or `null`. +//! Diesel `schema` / `DbTaskRow` follow `htyuc_models` layout (migrations + `diesel print-schema`). + +pub mod schema; +pub mod models; + +pub use models::{ + all_tasks, all_tasks_page, delete_task_by_id, find_task_by_id, one_pending_task, one_zombie_task, + upsert_task_row, DbTaskRow, +}; + +mod common_task_result; +mod payloads; +mod redis; +mod req_cron; +mod req_task; +mod req_tasks_with_page; +mod task_status; +mod task_type; + +pub use common_task_result::CommonTaskResult; +pub use req_cron::ReqCron; +pub use payloads::{ + AiScorePayload, AudioFileAndAiScorePayload, AudioPayload, PicturePayload, + try_parse_ai_score_payload, try_parse_video_compression_payload, try_parse_watermark_payload, + VideoCompressionPayload, WatermarkPayload, +}; +pub use redis::{sudoer_cache_redis_key, task_payload_redis_key, TS_REDIS_PREFIX}; +pub use req_task::ReqTask; +pub use req_tasks_with_page::ReqTasksWithPage; +pub use task_status::TaskStatus; +pub use task_type::TaskType; diff --git a/htyts_models/src/models.rs b/htyts_models/src/models.rs new file mode 100644 index 0000000..4155b09 --- /dev/null +++ b/htyts_models/src/models.rs @@ -0,0 +1,163 @@ +//! Diesel row type for `dbtask` (Hibernate `DbTask`) and DB access (aligned with `htyuc_models` patterns). + +use chrono::Duration; +use chrono::NaiveDateTime; +use diesel::dsl::count_star; +use diesel::pg::PgConnection; +use diesel::prelude::*; +use diesel::OptionalExtension; +use htycommons::db::PgPool; + +use crate::schema::dbtask; +use crate::schema::dbtask::dsl::*; + +/// One row of the Hibernate `DbTask` entity table. +#[derive(Debug, Clone, Queryable, Selectable, Identifiable, Insertable, AsChangeset)] +#[diesel(table_name = dbtask)] +#[diesel(primary_key(task_id))] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct DbTaskRow { + pub task_id: String, + pub hty_id: Option, + pub task_type: String, + pub task_status: String, + pub duration: Option, + pub created_by: String, + pub created_at: NaiveDateTime, + pub updated_by: String, + pub updated_at: NaiveDateTime, + pub meta: Option, +} + +impl DbTaskRow { + pub fn upsert(conn: &mut PgConnection, row: &DbTaskRow) -> anyhow::Result<()> { + let exists: Option = dbtask + .filter(task_id.eq(&row.task_id)) + .select(task_id) + .first(conn) + .optional()?; + if exists.is_some() { + diesel::update(dbtask.filter(task_id.eq(&row.task_id))) + .set(( + hty_id.eq(&row.hty_id), + task_type.eq(&row.task_type), + task_status.eq(&row.task_status), + duration.eq(row.duration), + created_by.eq(&row.created_by), + created_at.eq(row.created_at), + updated_by.eq(&row.updated_by), + updated_at.eq(row.updated_at), + meta.eq(&row.meta), + )) + .execute(conn)?; + } else { + diesel::insert_into(dbtask).values(row).execute(conn)?; + } + Ok(()) + } + + pub fn find_by_id(conn: &mut PgConnection, id: &str) -> anyhow::Result> { + Ok(dbtask + .filter(task_id.eq(id)) + .select(DbTaskRow::as_select()) + .first(conn) + .optional()?) + } + + pub fn delete_by_id(conn: &mut PgConnection, id: &str) -> anyhow::Result { + Ok(diesel::delete(dbtask.filter(task_id.eq(id))).execute(conn)?) + } + + pub fn one_pending(conn: &mut PgConnection) -> anyhow::Result> { + Ok(dbtask + .filter(task_status.eq("PENDING")) + .order(created_at.desc()) + .select(DbTaskRow::as_select()) + .first(conn) + .optional()?) + } + + pub fn one_zombie( + conn: &mut PgConnection, + zombie_minutes: i64, + ) -> anyhow::Result> { + let cutoff = chrono::Utc::now().naive_utc() - Duration::minutes(zombie_minutes); + Ok(dbtask + .filter( + task_status + .eq_any(vec!["PENDING".to_string(), "PROCESSING".to_string()]) + .and(created_at.lt(cutoff)), + ) + .order(created_at.asc()) + .select(DbTaskRow::as_select()) + .first(conn) + .optional()?) + } + + pub fn all(conn: &mut PgConnection) -> anyhow::Result> { + Ok(dbtask + .order(created_at.desc()) + .select(DbTaskRow::as_select()) + .load(conn)?) + } + + pub fn all_page( + conn: &mut PgConnection, + page: i64, + page_size: i64, + ) -> anyhow::Result<(Vec, i64)> { + let total: i64 = dbtask.select(count_star()).get_result(conn)?; + let total_pages = if page_size <= 0 { + 1 + } else { + (total + page_size - 1) / page_size + }; + let offset = (page - 1).max(0) * page_size; + let rows = dbtask + .order(created_at.desc()) + .offset(offset) + .limit(page_size) + .select(DbTaskRow::as_select()) + .load(conn)?; + Ok((rows, total_pages)) + } +} + +pub fn upsert_task_row(pool: &PgPool, row: &DbTaskRow) -> anyhow::Result<()> { + let mut conn = pool.get()?; + DbTaskRow::upsert(&mut conn, row) +} + +pub fn find_task_by_id(pool: &PgPool, id: &str) -> anyhow::Result> { + let mut conn = pool.get()?; + DbTaskRow::find_by_id(&mut conn, id) +} + +pub fn delete_task_by_id(pool: &PgPool, id: &str) -> anyhow::Result { + let mut conn = pool.get()?; + DbTaskRow::delete_by_id(&mut conn, id) +} + +pub fn one_pending_task(pool: &PgPool) -> anyhow::Result> { + let mut conn = pool.get()?; + DbTaskRow::one_pending(&mut conn) +} + +pub fn one_zombie_task(pool: &PgPool, zombie_minutes: i64) -> anyhow::Result> { + let mut conn = pool.get()?; + DbTaskRow::one_zombie(&mut conn, zombie_minutes) +} + +pub fn all_tasks(pool: &PgPool) -> anyhow::Result> { + let mut conn = pool.get()?; + DbTaskRow::all(&mut conn) +} + +pub fn all_tasks_page( + pool: &PgPool, + page: i64, + page_size: i64, +) -> anyhow::Result<(Vec, i64)> { + let mut conn = pool.get()?; + DbTaskRow::all_page(&mut conn, page, page_size) +} diff --git a/htyts_models/src/payloads/ai_score.rs b/htyts_models/src/payloads/ai_score.rs new file mode 100644 index 0000000..27e2cab --- /dev/null +++ b/htyts_models/src/payloads/ai_score.rs @@ -0,0 +1,148 @@ +use crate::common_task_result::CommonTaskResult; +use anyhow::Context; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +/// Mirrors [AiScorePayload](task_commons/.../AiScorePayload.java). +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct AiScorePayload { + /// 现网部分 Redis 叶子缺该字段;Java 侧可为 null,反序列化缺省为空串。 + #[serde(default)] + pub reference_url: String, + pub compare_url: String, + pub audio_type: String, + pub ref_id: String, + pub ref_type: String, + pub score_from: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub task_result: Option, +} + +/// 与 Java `stringToTypedObject(..., AiScorePayload)` 一致;支持时间戳多层嵌套、缺 `reference_url` 的叶子。 +pub fn try_parse_ai_score_payload(v: &Value) -> anyhow::Result { + if let Ok(p) = serde_json::from_value::(v.clone()) { + return Ok(p); + } + let candidates = collect_ai_score_candidate_maps(v); + let best = pick_best_ai_score_candidate(candidates).ok_or_else(|| { + anyhow::anyhow!( + "AiScorePayload: expected flat JSON or nested object with compare_url, audio_type, ref_id, ref_type, score_from" + ) + })?; + serde_json::from_value(Value::Object(best)).context( + "AiScorePayload: found candidate object but fields do not match struct", + ) +} + +fn collect_ai_score_candidate_maps(v: &Value) -> Vec> { + let mut out = Vec::new(); + walk_collect_ai_score(v, &mut out); + out +} + +fn walk_collect_ai_score(v: &Value, out: &mut Vec>) { + match v { + Value::Object(map) => { + if object_is_ai_score_leaf(map) { + out.push(map.clone()); + } + for child in map.values() { + walk_collect_ai_score(child, out); + } + } + Value::Array(items) => { + for child in items { + walk_collect_ai_score(child, out); + } + } + _ => {} + } +} + +fn object_is_ai_score_leaf(map: &serde_json::Map) -> bool { + let need = [ + "compare_url", + "audio_type", + "ref_id", + "ref_type", + "score_from", + ]; + for k in need { + if !map.get(k).map(|x| x.is_string()).unwrap_or(false) { + return false; + } + } + true +} + +fn pick_best_ai_score_candidate( + mut maps: Vec>, +) -> Option> { + if maps.is_empty() { + return None; + } + if maps.len() == 1 { + return Some(maps.remove(0)); + } + maps.sort_by_key(|m| { + let has_ref = m + .get("reference_url") + .and_then(|x| x.as_str()) + .map(|s| !s.is_empty()) + .unwrap_or(false); + std::cmp::Reverse(has_ref) + }); + Some(maps.remove(0)) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn parse_flat_missing_reference_url() { + let v = json!({ + "compare_url": "https://x/c.mp4", + "audio_type": "INSTRUMENT", + "ref_id": "rid", + "ref_type": "Comment", + "score_from": "MachineLearning" + }); + let p = try_parse_ai_score_payload(&v).expect("flat"); + assert_eq!(p.reference_url, ""); + assert_eq!(p.compare_url, "https://x/c.mp4"); + } + + #[test] + fn parse_nested_timestamps_like_redis() { + let v = json!({ + "2024-06-14T14:00:15.485144018": { + "compare_url": "https://upyun.example.com/a.mov", + "audio_type": "INSTRUMENT", + "ref_id": "b3", + "ref_type": "Comment", + "score_from": "MachineLearning", + "reference_url": "https://ref" + } + }); + let p = try_parse_ai_score_payload(&v).expect("nested"); + assert_eq!(p.compare_url, "https://upyun.example.com/a.mov"); + assert_eq!(p.reference_url, "https://ref"); + } + + #[test] + fn parse_nested_missing_reference_url() { + let v = json!({ + "2024-06-14T14:00:15.485144018": { + "compare_url": "https://upyun.example.com/a.mov", + "audio_type": "INSTRUMENT", + "ref_id": "b3", + "ref_type": "Comment", + "score_from": "MachineLearning" + } + }); + let p = try_parse_ai_score_payload(&v).expect("nested no ref"); + assert_eq!(p.reference_url, ""); + } +} diff --git a/htyts_models/src/payloads/audio.rs b/htyts_models/src/payloads/audio.rs new file mode 100644 index 0000000..c193091 --- /dev/null +++ b/htyts_models/src/payloads/audio.rs @@ -0,0 +1,10 @@ +use serde::{Deserialize, Serialize}; + +/// Mirrors [AudioPayload](task_commons/.../AudioPayload.java). +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct AudioPayload { + pub media_id: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_shifan: Option, + pub ref_resource_id: String, +} diff --git a/htyts_models/src/payloads/audio_file_ai_score.rs b/htyts_models/src/payloads/audio_file_ai_score.rs new file mode 100644 index 0000000..d920302 --- /dev/null +++ b/htyts_models/src/payloads/audio_file_ai_score.rs @@ -0,0 +1,12 @@ +use crate::payloads::ai_score::AiScorePayload; +use crate::payloads::audio::AudioPayload; +use serde::{Deserialize, Serialize}; + +/// Mirrors Java `AudioFileAndAiScorePayload` (fields are camelCase in JSON-B). +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct AudioFileAndAiScorePayload { + #[serde(rename = "aiScorePayload")] + pub ai_score_payload: AiScorePayload, + #[serde(rename = "audioPayload")] + pub audio_payload: AudioPayload, +} diff --git a/htyts_models/src/payloads/mod.rs b/htyts_models/src/payloads/mod.rs new file mode 100644 index 0000000..51f0c7c --- /dev/null +++ b/htyts_models/src/payloads/mod.rs @@ -0,0 +1,13 @@ +mod ai_score; +mod audio; +mod audio_file_ai_score; +mod picture; +mod video_compression; +mod watermark; + +pub use ai_score::{try_parse_ai_score_payload, AiScorePayload}; +pub use audio::AudioPayload; +pub use audio_file_ai_score::AudioFileAndAiScorePayload; +pub use picture::PicturePayload; +pub use video_compression::{try_parse_video_compression_payload, VideoCompressionPayload}; +pub use watermark::{try_parse_watermark_payload, WatermarkPayload}; diff --git a/htyts_models/src/payloads/picture.rs b/htyts_models/src/payloads/picture.rs new file mode 100644 index 0000000..f39c495 --- /dev/null +++ b/htyts_models/src/payloads/picture.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +/// Mirrors [PicturePayload](task_commons/.../PicturePayload.java). +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct PicturePayload { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hty_id: Option, + #[serde(default)] + pub images: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub image_origin: Option, +} diff --git a/htyts_models/src/payloads/video_compression.rs b/htyts_models/src/payloads/video_compression.rs new file mode 100644 index 0000000..e2d8659 --- /dev/null +++ b/htyts_models/src/payloads/video_compression.rs @@ -0,0 +1,114 @@ +use crate::common_task_result::CommonTaskResult; +use anyhow::Context; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +/// Mirrors [VideoCompressionPayload](task_commons/.../VideoCompressionPayload.java). +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct VideoCompressionPayload { + pub file_url: String, + pub resolution_type: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hty_resource_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub task_result: Option, +} + +/// Parse payload as Java `VideoCompressionPayload` does (`jsonb.fromJson` → flat struct). +/// +/// 现网 Redis 中可能存在历史嵌套形状(例如时间戳为 key 的层级对象),与 Java `task_commons` 中 +/// `String` 根字段一致;若直接反序列化失败,则在子树中递归查找同时含 `file_url` 与 `resolution_type` +/// 的对象,再反序列化,避免 Rust proc 与运营数据脱节。 +pub fn try_parse_video_compression_payload(v: &Value) -> anyhow::Result { + if let Ok(p) = serde_json::from_value(v.clone()) { + return Ok(p); + } + if let Some(inner) = find_nested_object_with_compression_fields(v) { + return serde_json::from_value(inner).context( + "VideoCompressionPayload: found candidate object but fields do not match struct", + ); + } + anyhow::bail!( + "VideoCompressionPayload: expected flat JSON or nested object containing file_url and resolution_type" + ); +} + +fn find_nested_object_with_compression_fields(v: &Value) -> Option { + match v { + Value::Object(map) => { + if object_has_compression_keys(map) { + return Some(Value::Object(map.clone())); + } + for child in map.values() { + if let Some(found) = find_nested_object_with_compression_fields(child) { + return Some(found); + } + } + None + } + Value::Array(items) => { + for child in items { + if let Some(found) = find_nested_object_with_compression_fields(child) { + return Some(found); + } + } + None + } + _ => None, + } +} + +fn object_has_compression_keys(map: &serde_json::Map) -> bool { + map.get("file_url").map(|x| x.is_string()).unwrap_or(false) + && map + .get("resolution_type") + .map(|x| x.is_string()) + .unwrap_or(false) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn parse_flat_matches_java() { + let v = json!({ + "file_url": "https://x/a.mp4", + "resolution_type": "HD", + "hty_resource_id": "r1", + "task_result": null + }); + let p = try_parse_video_compression_payload(&v).expect("flat"); + assert_eq!(p.file_url, "https://x/a.mp4"); + assert_eq!(p.resolution_type, "HD"); + assert_eq!(p.hty_resource_id.as_deref(), Some("r1")); + } + + #[test] + fn parse_nested_timestamp_keys_like_production() { + let v = json!({ + "2023-07-06T22:37:03.565085241": { + "2023-07-06T22:36:02.103486670": { + "2023-07-06T22:34:49.290672145": { + "file_url": "https://upyun.example.com/x.mp4", + "resolution_type": "HD", + "hty_resource_id": "7f287e25-6749-48d1-a15b-23ee394d9e04", + "task_result": { + "task_status": "PENDING", + "task_id": "6a184d13-9ac5-4d3d-876d-c5629746ecfd" + } + } + } + } + }); + let p = try_parse_video_compression_payload(&v).expect("nested"); + assert_eq!(p.file_url, "https://upyun.example.com/x.mp4"); + assert_eq!(p.resolution_type, "HD"); + assert_eq!( + p.hty_resource_id.as_deref(), + Some("7f287e25-6749-48d1-a15b-23ee394d9e04") + ); + assert!(p.task_result.is_some()); + } +} diff --git a/htyts_models/src/payloads/watermark.rs b/htyts_models/src/payloads/watermark.rs new file mode 100644 index 0000000..efa7808 --- /dev/null +++ b/htyts_models/src/payloads/watermark.rs @@ -0,0 +1,95 @@ +use crate::common_task_result::CommonTaskResult; +use anyhow::Context; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +/// Mirrors [WatermarkPayload](task_commons/.../WatermarkPayload.java). +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct WatermarkPayload { + pub video_url: String, + pub user_id: String, + pub ref_resource_id: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub task_result: Option, +} + +/// 与 Java `WatermarkPayload` 一致;现网 Redis 中可能存在时间戳多层嵌套,与 `try_parse_video_compression_payload` 同模式。 +pub fn try_parse_watermark_payload(v: &Value) -> anyhow::Result { + if let Ok(p) = serde_json::from_value(v.clone()) { + return Ok(p); + } + if let Some(inner) = find_nested_object_with_watermark_fields(v) { + return serde_json::from_value(inner).context( + "WatermarkPayload: found candidate object but fields do not match struct", + ); + } + anyhow::bail!( + "WatermarkPayload: expected flat JSON or nested object containing video_url, user_id, ref_resource_id" + ); +} + +fn find_nested_object_with_watermark_fields(v: &Value) -> Option { + match v { + Value::Object(map) => { + if object_has_watermark_keys(map) { + return Some(Value::Object(map.clone())); + } + for child in map.values() { + if let Some(found) = find_nested_object_with_watermark_fields(child) { + return Some(found); + } + } + None + } + Value::Array(items) => { + for child in items { + if let Some(found) = find_nested_object_with_watermark_fields(child) { + return Some(found); + } + } + None + } + _ => None, + } +} + +fn object_has_watermark_keys(map: &serde_json::Map) -> bool { + map.get("video_url").map(|x| x.is_string()).unwrap_or(false) + && map.get("user_id").map(|x| x.is_string()).unwrap_or(false) + && map + .get("ref_resource_id") + .map(|x| x.is_string()) + .unwrap_or(false) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn parse_flat() { + let v = json!({ + "video_url": "https://x/v.mp4", + "user_id": "u1", + "ref_resource_id": "r1" + }); + let p = try_parse_watermark_payload(&v).expect("flat"); + assert_eq!(p.video_url, "https://x/v.mp4"); + assert_eq!(p.user_id, "u1"); + assert_eq!(p.ref_resource_id, "r1"); + } + + #[test] + fn parse_nested_timestamps() { + let v = json!({ + "2023-07-06T22:37:03.565085241": { + "video_url": "https://upyun.example.com/a.mp4", + "user_id": "uid", + "ref_resource_id": "refid" + } + }); + let p = try_parse_watermark_payload(&v).expect("nested"); + assert_eq!(p.video_url, "https://upyun.example.com/a.mp4"); + } +} diff --git a/htyts_models/src/redis.rs b/htyts_models/src/redis.rs new file mode 100644 index 0000000..a9231f3 --- /dev/null +++ b/htyts_models/src/redis.rs @@ -0,0 +1,16 @@ +//! Redis key layout aligned with Java `BaseRedisService.TS_REDIS_PREFIX` (`task_commons`). + +/// Same prefix as Java `BaseRedisService.TS_REDIS_PREFIX` (`"TS_"`). +pub const TS_REDIS_PREFIX: &str = "TS_"; + +/// Full key for task payload string: `TS_` + `task_id`. +#[must_use] +pub fn task_payload_redis_key(task_id: &str) -> String { + format!("{TS_REDIS_PREFIX}{task_id}") +} + +/// Sudoer token cache: `TS_` + `SUDO_T_` + `token_id` (Java `RedisTokenCacheService` + `BaseRedisService`). +#[must_use] +pub fn sudoer_cache_redis_key(token_id: &str) -> String { + format!("{TS_REDIS_PREFIX}SUDO_T_{token_id}") +} diff --git a/htyts_models/src/req_cron.rs b/htyts_models/src/req_cron.rs new file mode 100644 index 0000000..0a77ba4 --- /dev/null +++ b/htyts_models/src/req_cron.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + +/// Java `ReqCron` (`expr` optional). +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct ReqCron { + #[serde(default)] + pub expr: Option, +} diff --git a/htyts_models/src/req_task.rs b/htyts_models/src/req_task.rs new file mode 100644 index 0000000..5e732f4 --- /dev/null +++ b/htyts_models/src/req_task.rs @@ -0,0 +1,60 @@ +use crate::task_status::TaskStatus; +use crate::task_type::TaskType; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; +use serde_json::Value as JsonValue; + +/// Mirrors Java `ReqTask`. +/// +/// `payload` is polymorphic in Java (`Optional`); stored in Redis as JSON per task type. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct ReqTask { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub task_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hty_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_at: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub updated_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub updated_at: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub task_type: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub task_status: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub duration: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub meta: Option, + /// Raw JSON payload (task-type-specific struct on the wire). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub payload: Option, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn serde_req_task_roundtrip() { + let sample = ReqTask { + task_id: Some("550e8400-e29b-41d4-a716-446655440000".into()), + task_type: Some(TaskType::Noop), + task_status: Some(TaskStatus::Pending), + payload: None, + hty_id: None, + created_by: None, + created_at: None, + updated_by: None, + updated_at: None, + duration: None, + meta: None, + }; + let json = serde_json::to_string(&sample).expect("serialize"); + let back: ReqTask = serde_json::from_str(&json).expect("deserialize"); + assert_eq!(back.task_id, sample.task_id); + } +} diff --git a/htyts_models/src/req_tasks_with_page.rs b/htyts_models/src/req_tasks_with_page.rs new file mode 100644 index 0000000..710e99e --- /dev/null +++ b/htyts_models/src/req_tasks_with_page.rs @@ -0,0 +1,11 @@ +use crate::req_task::ReqTask; +use serde::{Deserialize, Serialize}; + +/// Mirrors [ReqTasksWithPage](task_commons/src/main/java/cn/alchemystudio/taskcommons/web/ReqTasksWithPage.java). +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ReqTasksWithPage { + pub tasks: Vec, + /// Java bean property `totalPage`. + #[serde(rename = "totalPage")] + pub total_page: i32, +} diff --git a/htyts_models/src/schema.rs b/htyts_models/src/schema.rs new file mode 100644 index 0000000..ecebbb3 --- /dev/null +++ b/htyts_models/src/schema.rs @@ -0,0 +1,16 @@ +// @generated automatically by Diesel CLI. + +diesel::table! { + dbtask (task_id) { + task_id -> Text, + hty_id -> Nullable, + task_type -> Text, + task_status -> Text, + duration -> Nullable, + created_by -> Text, + created_at -> Timestamp, + updated_by -> Text, + updated_at -> Timestamp, + meta -> Nullable, + } +} diff --git a/htyts_models/src/task_status.rs b/htyts_models/src/task_status.rs new file mode 100644 index 0000000..34401cd --- /dev/null +++ b/htyts_models/src/task_status.rs @@ -0,0 +1,42 @@ +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +/// Mirrors `task_commons` `TaskStatus`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum TaskStatus { + Noop, + Done, + Pending, + Processing, + Failed, +} + +impl FromStr for TaskStatus { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "NOOP" => Ok(Self::Noop), + "DONE" => Ok(Self::Done), + "PENDING" => Ok(Self::Pending), + "PROCESSING" => Ok(Self::Processing), + "FAILED" => Ok(Self::Failed), + _ => Err(format!("unknown TaskStatus: {s}")), + } + } +} + +impl TaskStatus { + /// Database / Java `Enum.name()` string. + #[must_use] + pub fn as_db_str(self) -> &'static str { + match self { + Self::Noop => "NOOP", + Self::Done => "DONE", + Self::Pending => "PENDING", + Self::Processing => "PROCESSING", + Self::Failed => "FAILED", + } + } +} diff --git a/htyts_models/src/task_type.rs b/htyts_models/src/task_type.rs new file mode 100644 index 0000000..3eba973 --- /dev/null +++ b/htyts_models/src/task_type.rs @@ -0,0 +1,51 @@ +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +/// Mirrors Java `DbTask.TaskType`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum TaskType { + Noop, + UploadPicture, + ConvertAudioFile, + AiScore, + AudioFileAiScore, + Watermark, + VideoCompression, + TestUpyunRemove, +} + +impl FromStr for TaskType { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "NOOP" => Ok(Self::Noop), + "UPLOAD_PICTURE" => Ok(Self::UploadPicture), + "CONVERT_AUDIO_FILE" => Ok(Self::ConvertAudioFile), + "AI_SCORE" => Ok(Self::AiScore), + "AUDIO_FILE_AI_SCORE" => Ok(Self::AudioFileAiScore), + "WATERMARK" => Ok(Self::Watermark), + "VIDEO_COMPRESSION" => Ok(Self::VideoCompression), + "TEST_UPYUN_REMOVE" => Ok(Self::TestUpyunRemove), + _ => Err(format!("unknown TaskType: {s}")), + } + } +} + +impl TaskType { + /// Database / Java `Enum.name()` string. + #[must_use] + pub fn as_db_str(self) -> &'static str { + match self { + Self::Noop => "NOOP", + Self::UploadPicture => "UPLOAD_PICTURE", + Self::ConvertAudioFile => "CONVERT_AUDIO_FILE", + Self::AiScore => "AI_SCORE", + Self::AudioFileAiScore => "AUDIO_FILE_AI_SCORE", + Self::Watermark => "WATERMARK", + Self::VideoCompression => "VIDEO_COMPRESSION", + Self::TestUpyunRemove => "TEST_UPYUN_REMOVE", + } + } +} diff --git a/htyws/Cargo.toml b/htyws/Cargo.toml new file mode 100644 index 0000000..25da623 --- /dev/null +++ b/htyws/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "htyws" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +htycommons = { workspace = true } +htyuc_models = { workspace = true } +htyuc_remote = { workspace = true } +htyws_models = { path = "../htyws_models" } +anyhow = { workspace = true } +axum = { workspace = true } +axum-macros = { workspace = true } +chrono = { workspace = true } +diesel = { workspace = true } +dotenv = { workspace = true } +reqwest = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +time = { workspace = true } +tokio = { workspace = true } +tower-http = { workspace = true } +tracing = { workspace = true } diff --git a/htyws/README.md b/htyws/README.md new file mode 100644 index 0000000..d4dabb3 --- /dev/null +++ b/htyws/README.md @@ -0,0 +1,147 @@ +# 慧添翼考试 + +## 启动数据库 + +```bash +$ pg_ctl -D /usr/local/var/postgres start +``` + +## 创建数据库 + +```bash +$ psql postgres +postgres=# create database htyws owner hty encoding utf8; +CREATE DATABASE +postgres=# \q +``` + +## 创建用户 + +```bash +postgres=# create user hty; +CREATE ROLE +``` + +## 改变数据库owner + +```bash +postgres=# alter database htyws owner to hty; +ALTER DATABASE +postgres=# +``` + +## 改变tables的owner + +```bash +htyws=# \dt + List of relations + Schema | Name | Type | Owner +--------+----------------------------+-------+------- + public | __diesel_schema_migrations | table | htyws + public | rewards | table | weli + public | users | table | weli +(3 rows) +``` + +```bash +htyws=# alter table __diesel_schema_migrations owner to htyws; +ALTER TABLE +htyws=# alter table rewards owner to htyws; +ALTER TABLE +htyws=# alter table users owner to htyws; +ALTER TABLE +``` + +```bash +htyws=# \dt + List of relations + Schema | Name | Type | Owner +--------+----------------------------+-------+------- + public | __diesel_schema_migrations | table | htyws + public | rewards | table | htyws + public | users | table | htyws +(3 rows) + +htyws=# +``` + +## 安装diesel_cli + +```bash +$ cargo install diesel_cli --no-default-features --features postgres +``` + +## 初始化项目过程(已完成) + +创建`.env`文件: + +```bash +$ echo "DATABASE_URL=postgres://weli@localhost/htyws" > .env +``` + +里面是diesel要使用的数据库相关信息,上面就放数据库连接地址。 + +```bash +$ diesel setup +Creating migrations directory at: /Users/weli/works/htyws/migrations +``` + +## 创建用户表(初步工作完成) + +创建第一个migration,创建一张`users`表: + +```bash +$ diesel migration generate create_users +Creating migrations/2020-09-27-112838_create_users/up.sql +Creating migrations/2020-09-27-112838_create_users/down.sql +``` + +在`up.sql`里面添加内容: + +```sql +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + nickname VARCHAR UNIQUE NOT NULL +) +``` + +执行migration: + +```bash +$ diesel migration run 19:20:27 +Running migration 2020-09-27-112838_create_users +``` + +数据库表可以看到已经创建: + +```bash +postgres=# \c htyws +You are now connected to database "htyws" as user "weli". +htyws=# \dt + List of relations + Schema | Name | Type | Owner +--------+----------------------------+-------+------- + public | __diesel_schema_migrations | table | weli + public | users | table | weli +(2 rows) + +htyws=# select * from users; + id | nickname +----+---------- +(0 rows) + +htyws=# +``` + +## 生成/更新schema文件(每次重构要做) + +使用`diesel`的命令更新`schema.rs`(在项目根目录执行): + +```bash +$ diesel print-schema > src/schema.rs +``` + + + + + diff --git a/htyws/config/log4rs.yaml b/htyws/config/log4rs.yaml new file mode 100644 index 0000000..6216cdc --- /dev/null +++ b/htyws/config/log4rs.yaml @@ -0,0 +1,25 @@ +# log4rs_demo/config/log4rs.yaml + +refresh_rate: 5 seconds + +root: + level: debug + appenders: + - stdout +appenders: + stdout: + kind: console + encoder: + pattern: "{d(%+)(local)} [{t}] {h({l})} {M}:{m}{n}" + htyws_append: + kind: file + path: "logs/htyws.log" + encoder: + pattern: "{d(%+)(local)} [{t}] {h({l})} {M}:{m}{n}" + +loggers: + htyws: + level: debug + appenders: + - stdout + - htyws_append \ No newline at end of file diff --git a/htyws/container/Dockerfile b/htyws/container/Dockerfile new file mode 100644 index 0000000..1ca58ac --- /dev/null +++ b/htyws/container/Dockerfile @@ -0,0 +1,15 @@ +FROM debian:buster +COPY htyws/target/release/htyws /root/htyws +COPY htyws/container/*.sh /root/ +COPY htyws/config/log4rs.yaml /root/config/ +ARG OPERATOR +ARG OPERATOR_PWD +ARG FORM_API_KEY +ENV OPERATOR=$OPERATOR +ENV OPERATOR_PWD=$OPERATOR_PWD +ENV FORM_API_KEY=$FORM_API_KEY +RUN apt-get update -y +RUN apt-get install -y libpq-dev +RUN ls /root +RUN /root/setup.sh +ENTRYPOINT /root/init.sh diff --git a/htyws/container/init.sh b/htyws/container/init.sh new file mode 100755 index 0000000..704c4d3 --- /dev/null +++ b/htyws/container/init.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -x +#supervisord && supervisorctl start htyws +cd /root; ./htyws +sh diff --git a/htyws/container/local/Dockerfile b/htyws/container/local/Dockerfile new file mode 100644 index 0000000..b090817 --- /dev/null +++ b/htyws/container/local/Dockerfile @@ -0,0 +1,21 @@ +FROM ghcr.io/alchemy-studio-robot/htybasic +COPY htycommons ./htycommons +COPY htyws ./htyws +RUN cd htyws; cargo build --release + +FROM debian:buster +# copy boot program generated from above container +COPY --from=0 /htyws/target/release/htyws /root/htyws +# copy scripts file +COPY htyws/container/*.sh /root/ +COPY htyws/config/log4rs.yaml /root/config/ +ARG OPERATOR +ARG OPERATOR_PWD +ARG FORM_API_KEY +ENV OPERATOR=$OPERATOR +ENV OPERATOR_PWD=$OPERATOR_PWD +ENV FORM_API_KEY=$FORM_API_KEY +RUN apt-get update -y +RUN apt-get install -y libpq-dev +RUN /root/setup.sh +ENTRYPOINT /root/init.sh diff --git a/htyws/container/setup.sh b/htyws/container/setup.sh new file mode 100755 index 0000000..87e786d --- /dev/null +++ b/htyws/container/setup.sh @@ -0,0 +1,3 @@ +#!/bin/bash +set -x +chmod a+x /root/htyws diff --git a/htyws/logrotate.config b/htyws/logrotate.config new file mode 100644 index 0000000..734746b --- /dev/null +++ b/htyws/logrotate.config @@ -0,0 +1,9 @@ +compress + +"./htyws.log" { + hourly + rotate 12 + copytruncate + size 200M +} + diff --git a/htyws/src/lib.rs b/htyws/src/lib.rs new file mode 100644 index 0000000..b65b9ec --- /dev/null +++ b/htyws/src/lib.rs @@ -0,0 +1,307 @@ +// #![recursion_limit = "4000"] + +// #[macro_use] +// extern crate diesel; +// #[macro_use] +// extern crate serde_derive; +extern crate time; + +use std::sync::Arc; + +use axum::routing::{get, post}; +use axum::{Json, Router}; +use tracing::{debug, error}; + +use htycommons::common::HtyResponse; +use htycommons::db::{pool, DbState}; +use htycommons::web::{get_uc_url, wrap_json_anyhow_err, wrap_json_ok_resp}; +use tower_http::trace::TraceLayer; +use ws_all::*; +use ws_daka::*; +use ws_jihua::*; +use ws_lianxi::*; +mod notifications; + +pub mod r_ws; +// mod schema; +mod ws_all; +mod ws_daka; +mod ws_jihua; +mod ws_lianxi; + +async fn index() -> &'static str { + debug!("in server get_uc_url -> {}", get_uc_url()); + "-=HTYWS=-" +} + +// request HTYUC +async fn req_uc() -> Json> { + debug!("req_uc -> starts"); + let msg = raw_req_uc().await; + match msg { + Ok(ok) => wrap_json_ok_resp(ok), + Err(e) => { + error!("req_uc -> failed to req uc, e: {}", e); + wrap_json_anyhow_err(e) + } + } +} + +async fn raw_req_uc() -> anyhow::Result { + let client = reqwest::Client::new(); + + let msg = format!( + "ws -> uc ::: {}", + client + .get(format!("{}/{}", get_uc_url(), "index")) + .send() + .await? + .text() + .await? + ) + .as_str() + .to_string(); + + debug!("raw_req_uc -> {}", msg.as_str()); + + Ok(msg) +} + +pub fn ws_rocket(db_url: &str) -> Router { + let db_state = DbState { pool: pool(db_url) }; + + let shared_db_state = Arc::new(db_state); + + let app = Router::new() + .route( + "/api/v1/ws/find_all_course_sections", + get(find_all_course_sections), + ) + .route( + "/api/v1/ws/find_all_course_sections_by_created_by_with_page", + get(find_all_course_sections_by_created_by_with_page), + ) + .route( + "/api/v1/ws/find_all_course_sections_by_teacher_id_with_page", + get(find_all_course_sections_by_teacher_id_with_page), + ) + .route("/api/v1/ws/find_all_courses", get(find_all_courses)) + .route( + "/api/v1/ws/find_all_course_categories", + get(find_all_course_categories), + ) + .route( + "/api/v1/ws/find_all_students_by_teacher_id_and_status", + post(find_all_students_by_teacher_id_and_status), + ) + .route( + "/api/v1/ws/find_all_teachers_by_student_id/{student_id}", + get(find_all_teachers_by_student_id), + ) + .route("/api/v1/ws/find_jihua_by_id/{id}", get(find_jihua_by_id)) + .route("/api/v1/ws/find_jihua_by_id2/{id}", get(find_jihua_by_id2)) + .route("/api/v1/ws/find_daka_by_id/{id}", get(find_daka_by_id)) + .route("/api/v1/ws/find_daka_by_id2/{id}", get(find_daka_by_id2)) + .route( + "/api/v1/ws/find_jihuas_with_sections_by_user_id", + get(find_jihuas_with_sections_by_user_id), + ) + .route( + "/api/v1/ws/find_dakas_with_sections_by_user_id", + get(find_dakas_with_sections_by_user_id), + ) + .route("/api/v1/ws/find_course_by_id/{id}", get(find_course_by_id)) + .route( + "/api/v1/ws/find_course_section_by_id/{id}", + get(find_course_section_by_id), + ) + .route( + "/api/v1/ws/find_course_sections_by_ids", + post(find_course_sections_by_ids), + ) + .route( + "/api/v1/ws/find_jihuas_by_course_section_id/{id}", + get(find_jihuas_by_course_section_id), + ) + .route( + "/api/v1/ws/get_ref_score_by_ref_id_and_task_id/{ref_id}/{task_id}", + get(get_ref_score_by_ref_id_and_task_id), + ) + .route( + "/api/v1/ws/get_comments_by_ref_id/{ref_id}", + get(get_comments_by_ref_id), + ) + .route( + "/api/v1/ws/find_scores_by_ref_id/{ref_id}", + get(find_scores_by_ref_id), + ) + .route( + "/api/v1/ws/get_resource_note_group_by_ref_id/{ref_id}", + get(get_resource_note_group_by_ref_id), + ) + .route( + "/api/v1/ws/find_section_by_coursename", + get(find_section_by_coursename), + ) + .route( + "/api/v1/ws/get_unclaimed_students", + get(get_unclaimed_students), + ) + .route("/api/v1/ws/index", get(index)) + .route("/api/v1/ws/req_uc", get(req_uc)) + .route( + "/api/v1/ws/link_teacher_student", + post(link_teacher_student), + ) + .route("/api/v1/ws/claim_student", post(claim_student)) + .route("/api/v1/ws/create_jihua", post(create_jihua)) + .route("/api/v1/ws/create_daka", post(create_daka)) + .route( + "/api/v1/ws/create_course_category", + post(create_course_category), + ) + .route( + "/api/v1/ws/update_course_category", + post(update_course_category), + ) + .route("/api/v1/ws/create_comment", post(create_comment)) + .route("/api/v1/ws/create_score", post(create_score)) + .route("/api/v1/ws/update_score", post(update_score)) + .route("/api/v1/ws/update_comment", post(update_comment)) + .route("/api/v1/ws/create_lianxi", post(create_lianxi)) + .route("/api/v1/ws/create_lianxi2", post(create_lianxi2)) + .route("/api/v1/ws/create_piyue", post(create_piyue)) + .route( + "/api/v1/ws/find_course_group_by_ids", + post(find_course_group_by_ids), + ) + .route( + "/api/v1/ws/find_course_group_by_hty_id/{hty_id}", + get(find_course_group_by_hty_id), + ) + .route( + "/api/v1/ws/create_course_group", + post(create_course_group), + ) + .route( + "/api/v1/ws/update_course_group", + post(update_course_group), + ) + .route( + "/api/v1/ws/delete_course_group/{id_delete}", + post(delete_course_group), + ) + .route("/api/v1/ws/update_piyue", post(update_piyue)) + .route("/api/v1/ws/create_piyue2", post(create_piyue2)) + .route("/api/v1/ws/create_course", post(create_course)) + .route("/api/v1/ws/create_course_section", post(create_course_section)) + .route("/api/v1/ws/notify", post(notify)) + .route( + "/api/v1/ws/delete_jihua_by_id/{id_delete}", + post(delete_jihua_by_id), + ) + .route( + "/api/v1/ws/delete_daka_by_id/{id_delete}", + post(delete_daka_by_id), + ) + .route( + "/api/v1/ws/delete_lianxi_by_id/{id_delete}", + post(delete_lianxi_by_id), + ) + .route( + "/api/v1/ws/delete_lianxi_by_id2/{id_delete}", + post(delete_lianxi_by_id2), + ) + .route( + "/api/v1/ws/delete_course_by_id/{id_delete}", + post(delete_course_by_id), + ) + .route( + "/api/v1/ws/delete_course_section_by_id/{id_delete}", + post(delete_course_section_by_id), + ) + .route("/api/v1/ws/disclaim_student", post(disclaim_student)) + .route( + "/api/v1/ws/delete_course_category/{id_delete}", + get(delete_course_category), + ) + .route("/api/v1/ws/update_jihua", post(update_jihua)) + .route("/api/v1/ws/update_daka", post(update_daka)) + .route( + "/api/v1/ws/update_jihua_course_section_relations", + post(update_jihua_course_section_relations), + ) + .route( + "/api/v1/ws/create_or_update_resource_note_group", + post(create_or_update_resource_note_group), + ) + .route("/api/v1/ws/update_lianxi_by_id", post(update_lianxi_by_id)) + // .route("/api/v1/ws/add_resource_notes", post(add_resource_notes)) + .route("/api/v1/ws/update_course", post(update_course)) + .route( + "/api/v1/ws/find_ref_resource_by_id/{id}", + get(find_ref_resource_by_id), + ) + .route( + "/api/v1/ws/set_ref_resource_compression_processed/{id}", + post(set_ref_resource_compression_processed), + ) + .route( + "/api/v1/ws/set_ref_resource_synced_with_hty_resource", + post(set_ref_resource_synced_with_hty_resource), + ) + .route( + "/api/v1/ws/set_ref_resource_not_synced_with_hty_resource", + post(set_ref_resource_not_synced_with_hty_resource), + ) + .route("/api/v1/ws/update_ref_resource", post(update_ref_resource)) + .route( + "/api/v1/ws/find_teacher_students_by_ids", + post(find_teacher_students_by_ids), + ) + .route("/api/v1/ws/create_ref_resource", post(create_ref_resource)) + .route( + "/api/v1/ws/create_versioned_data", + post(create_versioned_data), + ) + .route( + "/api/v1/ws/get_versioned_data_by_ref_id/{ref_id}", + get(get_versioned_data_by_ref_id), + ) + .route( + "/api/v1/ws/delete_versioned_data_by_id/{id}", + get(delete_versioned_data_by_id), + ) + .route( + "/api/v1/ws/find_ref_resources_by_hty_resource_id/{resource_id}", + get(find_ref_resources_by_hty_resource_id), + ) + .route( + "/api/v1/ws/find_draft_course_sections_of_user", + get(find_draft_course_sections_of_user), + ) + .route( + "/api/v1/ws/is_course_section_in_use/{course_section_id}", + get(is_course_section_in_use), + ) + .route( + "/api/v1/ws/update_course_section_by_id", + post(update_course_section_by_id), + ) + .route( + "/api/v1/ws/find_all_teacher_students", + get(find_all_teacher_students), + ) + .route( + "/api/v1/ws/update_teacher_student", + post(update_teacher_student), + ) + .route( + "/api/v1/ws/delete_teacher_student", + post(delete_teacher_student), + ) + .layer(TraceLayer::new_for_http()) + .with_state(shared_db_state); + + app +} diff --git a/htyws/src/main.rs b/htyws/src/main.rs new file mode 100644 index 0000000..67a5ffa --- /dev/null +++ b/htyws/src/main.rs @@ -0,0 +1,24 @@ +// use htycommons::logger::logger_init; +use dotenv::dotenv; +use htycommons::db::get_ws_db_url; +use htycommons::logger::logger_init; +use htycommons::web::{get_ws_port, launch_rocket}; +use htyws; +use htyws::ws_rocket; + +#[tokio::main] +async fn main() { + dotenv().ok(); + + logger_init(); + + let port = get_ws_port().unwrap_or_else(|e| { + eprintln!("Failed to get WS_PORT: {}", e); + std::process::exit(1); + }); + let r = launch_rocket(port, ws_rocket(&get_ws_db_url())); + let _ = r.await; + + // this is reachable only after `Shutdown::notify()` or `Ctrl+C`. + println!("Rocket: deorbit."); +} diff --git a/htyws/src/r_ws.rs b/htyws/src/r_ws.rs new file mode 100644 index 0000000..0666700 --- /dev/null +++ b/htyws/src/r_ws.rs @@ -0,0 +1,16 @@ +use crate::ws_rocket; +use dotenv::dotenv; +use htycommons::db::get_ws_db_url; +use htycommons::web::{get_ws_port, launch_rocket}; + +#[tokio::main] +pub async fn main() { + dotenv().ok(); + + let port = get_ws_port().unwrap_or_else(|e| { + eprintln!("Failed to get WS_PORT: {}", e); + std::process::exit(1); + }); + let rocket = launch_rocket(port, ws_rocket(&get_ws_db_url())); + let _ = rocket.await; +} diff --git a/htyws/src/ws_lianxi.rs b/htyws/src/ws_lianxi.rs new file mode 100644 index 0000000..7194024 --- /dev/null +++ b/htyws/src/ws_lianxi.rs @@ -0,0 +1,511 @@ +use anyhow::anyhow; +use axum::extract::{Path, State}; +use axum::Json; +use axum_macros::debug_handler; +use htycommons::common::{current_local_datetime, HtyErr, HtyErrCode, HtyResponse}; +use htycommons::db::{extract_conn, fetch_db_conn, DbState}; +use htycommons::uuid; +use htycommons::web::{ + wrap_json_anyhow_err, wrap_json_ok_resp, HtyHostHeader, HtySudoerTokenHeader, +}; +use htyws_models::models::{ + Daka, DakaCourseSection, Jihua, JihuaCourseSection, Lianxi, Piyue, PiyueInfo, RefResource, + ReqLianxi, ReqLianxi2, +}; +use std::ops::DerefMut; +use std::sync::Arc; +use tracing::{debug, error}; + +pub fn count_lianxi_and_piyue_on_the_fly(all_lianxis: &Vec) -> (i32, i32) { + let count_lianxi = all_lianxis.clone().len() as i32; + let not_piyue_count = all_lianxis + .iter() + .filter(|lianxi| lianxi.has_piyue.is_none() || !lianxi.has_piyue.unwrap_or(false)) + .count() as i32; + (count_lianxi, not_piyue_count) +} + +// #[post( +// "/create_lianxi", +// format = "application/json", +// data = "" +// )] +#[debug_handler] +pub async fn create_lianxi( + root: HtySudoerTokenHeader, + host: HtyHostHeader, + State(db_pool): State>, + Json(in_lianxi): Json, +) -> Json> { + debug!("create_lianxi -> starts"); + + match raw_create_lianxi(&host, &root, &in_lianxi, db_pool).await { + Ok(ok) => { + debug!("create_lianxi -> success to create lianxi, e: {}", ok); + wrap_json_ok_resp(ok) + } + Err(e) => { + error!("create_lianxi -> failed to create lianxi, e: {}", e); + wrap_json_anyhow_err(e) + } + } +} + +pub async fn raw_create_lianxi( + _host: &HtyHostHeader, + _root: &HtySudoerTokenHeader, + in_req_lianxi: &ReqLianxi, + db_pool: Arc, +) -> anyhow::Result { + let req_lianxi = in_req_lianxi.clone(); + + let mut in_jihua_course_section_id = None; + let mut in_daka_course_section_id = None; + + if let Some(id) = &req_lianxi.jihua_course_section_id { + in_jihua_course_section_id = Some(id.clone()); + } + + if let Some(id) = &req_lianxi.daka_course_section_id { + in_daka_course_section_id = Some(id.clone()); + } + + let insert_lianxi = Lianxi { + id: uuid(), + video_url: req_lianxi.video_url.clone(), // todo: create ref_resource + video_id: req_lianxi.id.clone(), + created_at: Some(current_local_datetime()), + jihua_course_section_id: in_jihua_course_section_id.clone(), + daka_course_section_id: in_daka_course_section_id.clone(), + audio_question_url: req_lianxi.audio_question_url.clone(), + audio_question_id: req_lianxi.audio_question_id.clone(), + text_question: req_lianxi.text_question.clone(), + task: req_lianxi.task.clone(), + qupu_id: req_lianxi.qupu_id.clone(), // todo: create ref_resource + qupu_url: req_lianxi.qupu_url.clone(), + lianxi_type: req_lianxi.lianxi_type.clone(), + has_piyue: Some(false), + is_delete: Some(false), + created_by: req_lianxi.created_by.clone(), + creator_name: req_lianxi.creator_name.clone(), + }; + + let res = Lianxi::create( + &insert_lianxi, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + ); + + match res { + Ok(res_lianxi) => { + // lianxi created + // get jihua of this lianxi + // update lianxi_count + 1 + + debug!("update lianxi_count"); + if let Some(section_id) = &in_jihua_course_section_id { + let jihua_course_section = JihuaCourseSection::find_by_id( + section_id, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + let belonging_jihua = Jihua::find_by_id( + &jihua_course_section.jihua_id, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + let _ = belonging_jihua + .update_count(extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + } else if let Some(section_id) = &in_daka_course_section_id { + // in_daka_course_section_id is some + let daka_course_section = DakaCourseSection::find_by_id( + section_id, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + let belonging_daka = Daka::find_by_id( + &daka_course_section.daka_id, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + let _ = belonging_daka + .update_count(extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + } + + Ok(res_lianxi.id) + } + Err(e) => Err(anyhow!(HtyErr { + code: HtyErrCode::WebErr, + reason: Some("Fail to create lianxi e: ".to_string() + &e.to_string()), + })), + } +} + +#[debug_handler] +pub async fn create_lianxi2( + root: HtySudoerTokenHeader, + host: HtyHostHeader, + State(db_pool): State>, + Json(in_lianxi): Json, +) -> Json> { + debug!("create_lianxi2 -> starts"); + + match raw_create_lianxi2(&host, &root, &in_lianxi, db_pool).await { + Ok(ok) => { + debug!("create_lianxi2 -> success to create lianxi, e: {}", ok); + wrap_json_ok_resp(ok) + } + Err(e) => { + error!("create_lianxi2 -> failed to create lianxi, e: {}", e); + wrap_json_anyhow_err(e) + } + } +} + +pub async fn raw_create_lianxi2( + _host: &HtyHostHeader, + _root: &HtySudoerTokenHeader, + in_req_lianxi: &ReqLianxi2, + db_pool: Arc, +) -> anyhow::Result { + let req_lianxi = in_req_lianxi.clone(); + + let mut in_jihua_course_section_id = None; + let mut in_daka_course_section_id = None; + + if let Some(id) = &req_lianxi.jihua_course_section_id { + in_jihua_course_section_id = Some(id.clone()); + } + + if let Some(id) = &req_lianxi.daka_course_section_id { + in_daka_course_section_id = Some(id.clone()); + } + + let id_lianxi = uuid(); + + let insert_lianxi = Lianxi { + id: id_lianxi.clone(), + video_url: None, + video_id: None, + created_at: Some(current_local_datetime()), + jihua_course_section_id: in_jihua_course_section_id.clone(), + daka_course_section_id: in_daka_course_section_id.clone(), + audio_question_url: None, + audio_question_id: None, + text_question: req_lianxi.text_question.clone(), + task: None, + qupu_id: None, + qupu_url: None, // todo remove these `None` fields from database Lianxi table. + lianxi_type: req_lianxi.lianxi_type.clone(), + has_piyue: Some(false), + is_delete: Some(false), + created_by: req_lianxi.created_by.clone(), + creator_name: req_lianxi.creator_name.clone(), + }; + + let some_in_ref_resources = req_lianxi.ref_resources.clone(); + + if let Some(ref_resources) = &some_in_ref_resources { + for in_ref_resource in ref_resources { + let to_create_ref_resource = RefResource { + id: uuid(), + hty_resource_id: in_ref_resource.hty_resource_id.clone(), + ref_id: Some(id_lianxi.clone()), + ref_type: Some("Lianxi".to_string()), + resource_url: in_ref_resource.resource_url.clone(), + resource_type: in_ref_resource.resource_type.clone(), + ref_name: in_ref_resource.ref_name.clone(), + ref_desc: in_ref_resource.ref_desc.clone(), + meta: None, + tasks: in_ref_resource.tasks.clone(), + is_shifan: in_ref_resource.is_shifan.clone(), + created_at: in_ref_resource.created_at.clone(), + updated_at: in_ref_resource.updated_at.clone(), + compress_processed: in_ref_resource.compress_processed.clone(), + created_by: in_ref_resource.created_by.clone(), + synced_with_hty_resource: in_ref_resource.synced_with_hty_resource.clone(), + updated_by: in_ref_resource.updated_by.clone(), + }; + + debug!( + "raw_create_lianxi2 -> to_create_ref_resource / {:?}", + to_create_ref_resource + ); + let _ = RefResource::create( + &to_create_ref_resource, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + } + } + + // let audio_resource = RefResource { + // id: uuid(), + // hty_resource_id: req_lianxi.audio_question_id.clone(), + // ref_id: Some(id_lianxi.clone()), + // ref_type: Some("Lianxi".to_string()), + // resource_url: req_lianxi.audio_question_url.clone(), + // resource_type: Some("Audio".to_string()), + // ref_name: Some("练习录音".to_string()), + // ref_desc: None, + // meta: None, + // tasks: None, + // is_shifan: Some(false), + // }; + // + // let _ = RefResource::create(&audio_resource, extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + // + // let qupu_resource = RefResource { + // id: uuid(), + // hty_resource_id: req_lianxi.qupu_id.clone(), + // ref_id: Some(id_lianxi.clone()), + // ref_type: Some("Lianxi".to_string()), + // resource_url: req_lianxi.qupu_url.clone(), + // resource_type: Some("Picture".to_string()), + // ref_name: Some("练习曲谱".to_string()), + // ref_desc: None, + // meta: None, + // tasks: None, + // is_shifan: Some(false), + // }; + // + // let _ = RefResource::create(&qupu_resource, extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + // + // + // let video_resource = RefResource { + // id: uuid(), + // hty_resource_id: req_lianxi.video_id.clone(), + // ref_id: Some(id_lianxi.clone()), + // ref_type: Some("Lianxi".to_string()), + // resource_url: req_lianxi.video_url.clone(), + // resource_type: Some("Video".to_string()), + // ref_name: Some("练习视频".to_string()), + // ref_desc: None, + // meta: None, + // tasks: None, + // is_shifan: Some(false), + // }; + // + // let _ = RefResource::create(&video_resource, extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + + let res = Lianxi::create( + &insert_lianxi, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + ); + + match res { + Ok(res_lianxi) => { + // lianxi created + // get jihua of this lianxi + // update lianxi_count + 1 + + debug!("update lianxi_count"); + if let Some(section_id) = &in_jihua_course_section_id { + let jihua_course_section = JihuaCourseSection::find_by_id( + section_id, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + let belonging_jihua = Jihua::find_by_id( + &jihua_course_section.jihua_id, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + let _ = belonging_jihua + .update_count(extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + } else if let Some(section_id) = &in_daka_course_section_id { + // in_daka_course_section_id is some + let daka_course_section = DakaCourseSection::find_by_id( + section_id, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + let belonging_daka = Daka::find_by_id( + &daka_course_section.daka_id, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + let _ = belonging_daka + .update_count(extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + } + + Ok(res_lianxi.id) + } + Err(e) => Err(anyhow!(HtyErr { + code: HtyErrCode::WebErr, + reason: Some("Fail to create lianxi e: ".to_string() + &e.to_string()), + })), + } +} + +pub async fn delete_lianxi_by_id( + _root: HtySudoerTokenHeader, + Path(id_delete): Path, + State(db_pool): State>, +) -> Json> { + debug!("delete_lianxi_by_id -> start here"); + match raw_delete_lianxi_by_id(&id_delete, db_pool).await { + Ok(ok) => wrap_json_ok_resp(ok), + Err(e) => { + error!("delete_lianxi_by_id -> failed to delete lianxi, e: {}", e); + wrap_json_anyhow_err(e) + } + } +} + +pub async fn raw_delete_lianxi_by_id( + id_delete: &String, + db_pool: Arc, +) -> anyhow::Result { + let to_delete_lianxi = Lianxi::find_by_id( + id_delete, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + let piyues = + to_delete_lianxi.find_linked_piyues(extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + let resp = Lianxi::logic_delete_by_id( + id_delete, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + + // 所属的piyue和comments全都逻辑删除. + for mut to_delete_piyue in piyues { + // todo: piyue所属的piyue_info和comments都逻辑删除 + // comments还没弄,先做piyue和piyue_info的逻辑删除 + let piyue_infos = to_delete_piyue + .find_linked_piyue_infos(extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + for mut to_delete_piyue_info in piyue_infos { + to_delete_piyue_info.is_delete = Some(true); + let _ = PiyueInfo::update( + &to_delete_piyue_info, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + } + to_delete_piyue.is_delete = Some(true); + let _ = Piyue::update( + &to_delete_piyue, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + } + + if let Some(section_id) = &to_delete_lianxi.jihua_course_section_id { + let relation = JihuaCourseSection::find_by_id( + section_id, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + let belonging_jihua = Jihua::find_by_id( + &relation.jihua_id, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + let _ = belonging_jihua.update_count(extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + // 更新计划下属练习数量 + } + + Ok(resp) +} + +pub async fn delete_lianxi_by_id2( + _root: HtySudoerTokenHeader, + Path(id_delete): Path, + State(db_pool): State>, +) -> Json> { + debug!("delete_lianxi_by_id2 -> start here"); + match raw_delete_lianxi_by_id2(&id_delete, db_pool).await { + Ok(ok) => wrap_json_ok_resp(ok), + Err(e) => { + error!("delete_lianxi_by_id2 -> failed to delete lianxi, e: {}", e); + wrap_json_anyhow_err(e) + } + } +} + +pub async fn raw_delete_lianxi_by_id2( + id_delete: &String, + db_pool: Arc, +) -> anyhow::Result { + // let to_delete_lianxi = Lianxi::find_by_id(id_delete, extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + // let piyues = to_delete_lianxi.find_linked_piyues(extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + let deleted_lianxi = Lianxi::logic_delete_by_id( + id_delete, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + Ok(deleted_lianxi.to_req2()) + + // todo: 现在只需要逻辑删除掉lianxi就行了,因为下面挂的其他关联数据也就随着lianxi不在前端使用了. + + // // 所属的piyue和comments全都逻辑删除. + // for mut to_delete_piyue in piyues { +} + +// deprecated. NO USE +pub async fn update_lianxi_by_id( + _root: HtySudoerTokenHeader, + State(db_pool): State>, + Json(in_lianxi): Json, +) -> Json> { + debug!("update_lianxi_by_id -> start here"); + match raw_update_lianxi_by_id(&in_lianxi, db_pool).await { + Ok(ok) => { + debug!("update_lianxi_by_id -> success to update lianxi {}", ok); + wrap_json_ok_resp(ok) + } + Err(e) => { + error!("update_lianxi_by_id -> failed to update lianxi, e: {}", e); + wrap_json_anyhow_err(e) + } + } +} + +pub async fn raw_update_lianxi_by_id( + in_req_lianxi: &ReqLianxi, + db_pool: Arc, +) -> anyhow::Result { + let req_lianxi = in_req_lianxi.clone(); + if req_lianxi.jihua_course_section_id.is_none() && req_lianxi.daka_course_section_id.is_none() { + return Err(anyhow!(HtyErr { + code: HtyErrCode::WebErr, + reason: Some( + "jihua_course_section_id AND daka_course_section_id can not be null at same time" + .into() + ), + })); + } + + let id_lianxi = req_lianxi.id.ok_or_else(|| anyhow!("id is required"))?; + + let exist = Lianxi::verify_exist_by_id( + &id_lianxi, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + if !exist { + return Err(anyhow!(HtyErr { + code: HtyErrCode::WebErr, + reason: Some("no exist lianxi".into()), + })); + } + + // let db_lianxi = Lianxi::find_by_id(&id_lianxi, extract_conn(fetch_db_conn(&db_pool)?).deref_mut())?; + + let some_id_req_jihua_course_section = req_lianxi.jihua_course_section_id.clone(); + let some_id_req_daka_course_section = req_lianxi.daka_course_section_id.clone(); + // return Err(anyhow!(HtyErr { + // code: HtyErrCode::WebErr, + // reason: Some("jihua_course_section_id not equal to existing record".into()), + // })); + // } + + let update_lianxi = Lianxi { + id: id_lianxi, + video_url: req_lianxi.video_url, + video_id: req_lianxi.video_id, + created_at: req_lianxi.created_at, + jihua_course_section_id: some_id_req_jihua_course_section.clone(), + audio_question_url: req_lianxi.audio_question_url, + audio_question_id: req_lianxi.audio_question_id, + text_question: req_lianxi.text_question, + task: req_lianxi.task, + qupu_id: req_lianxi.qupu_id, + qupu_url: req_lianxi.qupu_url, + lianxi_type: req_lianxi.lianxi_type, + daka_course_section_id: some_id_req_daka_course_section.clone(), + has_piyue: req_lianxi.has_piyue, + is_delete: req_lianxi.is_delete, + created_by: req_lianxi.created_by, + creator_name: req_lianxi.creator_name, + }; + let res = Lianxi::update( + &update_lianxi, + extract_conn(fetch_db_conn(&db_pool)?).deref_mut(), + )?; + Ok(res.id) +} diff --git a/htyws/start.sh b/htyws/start.sh new file mode 100755 index 0000000..662a63b --- /dev/null +++ b/htyws/start.sh @@ -0,0 +1,10 @@ +#!/bin/sh +set -x + +echo "----------------------------" >> htyws.log +echo "$(date)" >> htyws.log +echo "----------------------------" >> htyws.log + +nohup cargo run >> htyws.log & + + diff --git a/htyws_models/Cargo.toml b/htyws_models/Cargo.toml new file mode 100644 index 0000000..47d90b9 --- /dev/null +++ b/htyws_models/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "htyws_models" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +htycommons = { workspace = true } +htyuc_models = { git = "https://github.com/alchemy-studio/AuthCore.git", package = "htyuc_models" } +anyhow = { workspace = true } +chrono = { workspace = true } +diesel = { workspace = true } +serde = { workspace = true } +serde_derive = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true } +tracing = { workspace = true } + diff --git a/htyws_models/diesel.toml b/htyws_models/diesel.toml new file mode 100644 index 0000000..4d69272 --- /dev/null +++ b/htyws_models/diesel.toml @@ -0,0 +1,5 @@ +# For documentation on how to configure this file, +# see diesel.rs/guides/configuring-diesel-cli + +[print_schema] +file = "src/schema.rs" # diff --git a/htyws_models/migrations/.gitkeep b/htyws_models/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/htyws_models/migrations/00000000000000_diesel_initial_setup/down.sql b/htyws_models/migrations/00000000000000_diesel_initial_setup/down.sql new file mode 100644 index 0000000..a9f5260 --- /dev/null +++ b/htyws_models/migrations/00000000000000_diesel_initial_setup/down.sql @@ -0,0 +1,6 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + +DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); +DROP FUNCTION IF EXISTS diesel_set_updated_at(); diff --git a/htyws_models/migrations/00000000000000_diesel_initial_setup/up.sql b/htyws_models/migrations/00000000000000_diesel_initial_setup/up.sql new file mode 100644 index 0000000..d68895b --- /dev/null +++ b/htyws_models/migrations/00000000000000_diesel_initial_setup/up.sql @@ -0,0 +1,36 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + + + + +-- Sets up a trigger for the given table to automatically set a column called +-- `updated_at` whenever the row is modified (unless `updated_at` was included +-- in the modified columns) +-- +-- # Example +-- +-- ```sql +-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); +-- +-- SELECT diesel_manage_updated_at('users'); +-- ``` +CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ +BEGIN + EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s + FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ +BEGIN + IF ( + NEW IS DISTINCT FROM OLD AND + NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at + ) THEN + NEW.updated_at := current_timestamp; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; diff --git a/htyws_models/migrations/2021-08-29-075531_init/down.sql b/htyws_models/migrations/2021-08-29-075531_init/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2021-08-29-075531_init/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2021-08-29-075531_init/up.sql b/htyws_models/migrations/2021-08-29-075531_init/up.sql new file mode 100644 index 0000000..9606a45 --- /dev/null +++ b/htyws_models/migrations/2021-08-29-075531_init/up.sql @@ -0,0 +1,191 @@ +-- Your SQL goes here + +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 13.3 +-- Dumped by pg_dump version 13.3 + + +CREATE TABLE lianxi +( + id character varying NOT NULL, + video_url character varying, + video_id character varying, + create_at timestamp without time zone, + jihua_qumu_section_id character varying NOT NULL, + audio_question_url character varying, + audio_question_id character varying, + text_question text +); + + +CREATE TABLE jihua +( + id character varying NOT NULL, + start_date timestamp without time zone NOT NULL, + end_date timestamp without time zone NOT NULL, + student_id character varying NOT NULL, + teacher_id character varying NOT NULL, + beizhu character varying +); + + +CREATE TABLE jihua_qumu_section +( + id character varying NOT NULL, + jihua_id character varying NOT NULL, + qumu_section_id character varying NOT NULL, + meta jsonb +); + + +CREATE TABLE piyue +( + id character varying NOT NULL, + lianxi_id character varying NOT NULL, + content jsonb NOT NULL, + teacher_id character varying NOT NULL, + rating jsonb +); + + +CREATE TABLE piyue_info +( + id character varying NOT NULL, + piyue_id character varying NOT NULL, + lianxi_video_pos jsonb, + qupu_pos jsonb, + comment text +); + + +CREATE TABLE qumu +( + name character varying NOT NULL, + id character varying NOT NULL, + qumu_type integer NOT NULL +); + + +CREATE TABLE qumu_info +( + id character varying NOT NULL, + meta jsonb, + qumu_id character varying NOT NULL, + created_by character varying NOT NULL +); + +CREATE TABLE qumu_section +( + id character varying NOT NULL, + created_by character varying NOT NULL, + qupu_id character varying, + qupu_url character varying, + shifan_id character varying, + shifan_url character varying, + section_name character varying NOT NULL, + qumu_id character varying NOT NULL, + task jsonb +); + +CREATE TABLE teacher_student +( + teacher_id character varying NOT NULL, + student_id character varying NOT NULL, + id character varying NOT NULL +); + + +ALTER TABLE ONLY piyue_info + ADD CONSTRAINT piyue_info_pk PRIMARY KEY (id); + + +ALTER TABLE ONLY piyue + ADD CONSTRAINT piyue_pk PRIMARY KEY (id); + + +ALTER TABLE ONLY jihua_qumu_section + ADD CONSTRAINT jihua_qumu_section_pk PRIMARY KEY (id); + + +ALTER TABLE ONLY qumu_info + ADD CONSTRAINT qumu_meta_pk PRIMARY KEY (id); + +ALTER TABLE ONLY qumu + ADD CONSTRAINT qumu_pk PRIMARY KEY (id); + + + +ALTER TABLE ONLY qumu_section + ADD CONSTRAINT qumu_section_pk PRIMARY KEY (id); + +ALTER TABLE ONLY teacher_student + ADD CONSTRAINT teacher_student_pk PRIMARY KEY (id); + + +ALTER TABLE ONLY jihua + ADD CONSTRAINT jihua_pk PRIMARY KEY (id); + + +ALTER TABLE ONLY lianxi + ADD CONSTRAINT lianxi_pk PRIMARY KEY (id); + + +CREATE UNIQUE INDEX piyue_id_uindex ON piyue USING btree (id); + + +CREATE UNIQUE INDEX piyue_info_id_uindex ON piyue_info USING btree (id); + + +CREATE UNIQUE INDEX qumu_id_uindex ON qumu USING btree (id); + + + +CREATE UNIQUE INDEX qumu_meta_id_uindex ON qumu_info USING btree (id); + + + +CREATE UNIQUE INDEX qumu_name_uindex ON qumu USING btree (name); + + + +CREATE UNIQUE INDEX jihua_id_uindex ON jihua USING btree (id); + + +CREATE UNIQUE INDEX lianxi_id_uindex ON lianxi USING btree (id); + + +ALTER TABLE ONLY lianxi + ADD CONSTRAINT jihua_qumu_section_id_fk FOREIGN KEY (jihua_qumu_section_id) REFERENCES jihua_qumu_section (id); + + +ALTER TABLE ONLY piyue_info + ADD CONSTRAINT piyue_info_piyue_id_fk FOREIGN KEY (piyue_id) REFERENCES piyue (id); + + +ALTER TABLE ONLY piyue + ADD CONSTRAINT piyue_lianxi_id_fk FOREIGN KEY (lianxi_id) REFERENCES lianxi (id); + + +ALTER TABLE ONLY jihua_qumu_section + ADD CONSTRAINT jihua_qumu_section_jihua_id_fk FOREIGN KEY (jihua_id) REFERENCES jihua (id); + + +ALTER TABLE ONLY jihua_qumu_section + ADD CONSTRAINT jihua_qumu_section_qumu_section_id_fk FOREIGN KEY (qumu_section_id) REFERENCES qumu_section (id); + + +ALTER TABLE ONLY qumu_info + ADD CONSTRAINT qumu_info_qumu_id_fk FOREIGN KEY (qumu_id) REFERENCES qumu (id); + + +ALTER TABLE ONLY qumu_section + ADD CONSTRAINT qumu_section_qumu_id_fk FOREIGN KEY (qumu_id) REFERENCES qumu (id); + + +-- +-- PostgreSQL database dump complete +-- + diff --git a/htyws_models/migrations/2021-12-27-085215_add_task_for_lianxi/down.sql b/htyws_models/migrations/2021-12-27-085215_add_task_for_lianxi/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2021-12-27-085215_add_task_for_lianxi/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2021-12-27-085215_add_task_for_lianxi/up.sql b/htyws_models/migrations/2021-12-27-085215_add_task_for_lianxi/up.sql new file mode 100644 index 0000000..35c9989 --- /dev/null +++ b/htyws_models/migrations/2021-12-27-085215_add_task_for_lianxi/up.sql @@ -0,0 +1,2 @@ +alter table lianxi + add task jsonb;-- Your SQL goes here \ No newline at end of file diff --git a/htyws_models/migrations/2021-12-30-113916_refactor_qumu/down.sql b/htyws_models/migrations/2021-12-30-113916_refactor_qumu/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2021-12-30-113916_refactor_qumu/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2021-12-30-113916_refactor_qumu/up.sql b/htyws_models/migrations/2021-12-30-113916_refactor_qumu/up.sql new file mode 100644 index 0000000..df7b829 --- /dev/null +++ b/htyws_models/migrations/2021-12-30-113916_refactor_qumu/up.sql @@ -0,0 +1,10 @@ +-- Your SQL goes here + +alter table qumu + add created_by varchar not null; + +alter table qumu + add created_at timestamp not null; + +drop index qumu_name_uindex; + diff --git a/htyws_models/migrations/2021-12-30-115719_drop_qumu_created_at_and_by_nullable/down.sql b/htyws_models/migrations/2021-12-30-115719_drop_qumu_created_at_and_by_nullable/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2021-12-30-115719_drop_qumu_created_at_and_by_nullable/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2021-12-30-115719_drop_qumu_created_at_and_by_nullable/up.sql b/htyws_models/migrations/2021-12-30-115719_drop_qumu_created_at_and_by_nullable/up.sql new file mode 100644 index 0000000..7005723 --- /dev/null +++ b/htyws_models/migrations/2021-12-30-115719_drop_qumu_created_at_and_by_nullable/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table qumu + alter column created_by drop not null; + +alter table qumu + alter column created_at drop not null; + diff --git a/htyws_models/migrations/2021-12-30-133507_add_qupu_id_and_qupu_url_for_lianxi/down.sql b/htyws_models/migrations/2021-12-30-133507_add_qupu_id_and_qupu_url_for_lianxi/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2021-12-30-133507_add_qupu_id_and_qupu_url_for_lianxi/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2021-12-30-133507_add_qupu_id_and_qupu_url_for_lianxi/up.sql b/htyws_models/migrations/2021-12-30-133507_add_qupu_id_and_qupu_url_for_lianxi/up.sql new file mode 100644 index 0000000..f41cb12 --- /dev/null +++ b/htyws_models/migrations/2021-12-30-133507_add_qupu_id_and_qupu_url_for_lianxi/up.sql @@ -0,0 +1,7 @@ +-- Your SQL goes here +alter table lianxi + add qupu_id varchar; + +alter table lianxi + add qupu_url varchar; + diff --git a/htyws_models/migrations/2022-01-10-155326_add_answer_for_piyue/down.sql b/htyws_models/migrations/2022-01-10-155326_add_answer_for_piyue/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-01-10-155326_add_answer_for_piyue/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-10-155326_add_answer_for_piyue/up.sql b/htyws_models/migrations/2022-01-10-155326_add_answer_for_piyue/up.sql new file mode 100644 index 0000000..388e867 --- /dev/null +++ b/htyws_models/migrations/2022-01-10-155326_add_answer_for_piyue/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +alter table piyue + add answer jsonb; \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-12-151953_add_serial_for_pityueinfo/down.sql b/htyws_models/migrations/2022-01-12-151953_add_serial_for_pityueinfo/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-01-12-151953_add_serial_for_pityueinfo/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-12-151953_add_serial_for_pityueinfo/up.sql b/htyws_models/migrations/2022-01-12-151953_add_serial_for_pityueinfo/up.sql new file mode 100644 index 0000000..282f66b --- /dev/null +++ b/htyws_models/migrations/2022-01-12-151953_add_serial_for_pityueinfo/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +alter table piyue_info + add serial varchar; \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-13-071350_add_lianxi_type/down.sql b/htyws_models/migrations/2022-01-13-071350_add_lianxi_type/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-01-13-071350_add_lianxi_type/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-13-071350_add_lianxi_type/up.sql b/htyws_models/migrations/2022-01-13-071350_add_lianxi_type/up.sql new file mode 100644 index 0000000..b866e4a --- /dev/null +++ b/htyws_models/migrations/2022-01-13-071350_add_lianxi_type/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +alter table lianxi + add lianxi_type varchar; + diff --git a/htyws_models/migrations/2022-01-13-073805_add_daka_tbl/down.sql b/htyws_models/migrations/2022-01-13-073805_add_daka_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-01-13-073805_add_daka_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-13-073805_add_daka_tbl/up.sql b/htyws_models/migrations/2022-01-13-073805_add_daka_tbl/up.sql new file mode 100644 index 0000000..9467d1e --- /dev/null +++ b/htyws_models/migrations/2022-01-13-073805_add_daka_tbl/up.sql @@ -0,0 +1,15 @@ +-- Your SQL goes here + +create table daka +( + id varchar not null + constraint daka_pk + primary key, + daka_date timestamp not null, + student_id varchar not null, + beizhu varchar +); + +create unique index daka_id_uindex + on daka (id); + diff --git a/htyws_models/migrations/2022-01-13-074314_add_daka_qumu_section_dbl/down.sql b/htyws_models/migrations/2022-01-13-074314_add_daka_qumu_section_dbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-01-13-074314_add_daka_qumu_section_dbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-13-074314_add_daka_qumu_section_dbl/up.sql b/htyws_models/migrations/2022-01-13-074314_add_daka_qumu_section_dbl/up.sql new file mode 100644 index 0000000..13eae82 --- /dev/null +++ b/htyws_models/migrations/2022-01-13-074314_add_daka_qumu_section_dbl/up.sql @@ -0,0 +1,18 @@ +-- Your SQL goes here +create table daka_qumu_section +( + id varchar not null + constraint daka_qumu_section_pk + primary key, + daka_id varchar not null + constraint daka_qumu_section_daka_id_fk + references daka, + qumu_section_id varchar not null + constraint daka_qumu_section_qumu_section_id_fk + references qumu_section, + meta jsonb +); + +create unique index daka_qumu_section_id_uindex + on daka_qumu_section (id); + diff --git a/htyws_models/migrations/2022-01-13-074849_add_daka_ref_to_lianxi/down.sql b/htyws_models/migrations/2022-01-13-074849_add_daka_ref_to_lianxi/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-01-13-074849_add_daka_ref_to_lianxi/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-13-074849_add_daka_ref_to_lianxi/up.sql b/htyws_models/migrations/2022-01-13-074849_add_daka_ref_to_lianxi/up.sql new file mode 100644 index 0000000..8dd8f3d --- /dev/null +++ b/htyws_models/migrations/2022-01-13-074849_add_daka_ref_to_lianxi/up.sql @@ -0,0 +1,11 @@ +-- Your SQL goes here +alter table lianxi + alter column jihua_qumu_section_id drop not null; + +alter table lianxi + add daka_qumu_section_id varchar; + +alter table lianxi + add constraint daka_qumu_section_id_fk + foreign key (daka_qumu_section_id) references daka_qumu_section; + diff --git a/htyws_models/migrations/2022-01-14-074613_add_ref_resources/down.sql b/htyws_models/migrations/2022-01-14-074613_add_ref_resources/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-01-14-074613_add_ref_resources/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-14-074613_add_ref_resources/up.sql b/htyws_models/migrations/2022-01-14-074613_add_ref_resources/up.sql new file mode 100644 index 0000000..0ed2458 --- /dev/null +++ b/htyws_models/migrations/2022-01-14-074613_add_ref_resources/up.sql @@ -0,0 +1,17 @@ +-- Your SQL goes here + +create table ref_resources +( + id varchar not null + constraint ref_resources_pk + primary key, + hty_resource_id varchar not null, + ref_id varchar not null, + ref_type varchar not null, + resource_url varchar, + resource_type varchar +); + +create unique index ref_resources_id_uindex + on ref_resources (id); + diff --git a/htyws_models/migrations/2022-01-14-143728_add_qumu_type/down.sql b/htyws_models/migrations/2022-01-14-143728_add_qumu_type/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-01-14-143728_add_qumu_type/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-14-143728_add_qumu_type/up.sql b/htyws_models/migrations/2022-01-14-143728_add_qumu_type/up.sql new file mode 100644 index 0000000..5b58057 --- /dev/null +++ b/htyws_models/migrations/2022-01-14-143728_add_qumu_type/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table qumu + alter column qumu_type type varchar using qumu_type::varchar; + diff --git a/htyws_models/migrations/2022-01-14-145755_qumu_type_nullable/down.sql b/htyws_models/migrations/2022-01-14-145755_qumu_type_nullable/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-01-14-145755_qumu_type_nullable/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-14-145755_qumu_type_nullable/up.sql b/htyws_models/migrations/2022-01-14-145755_qumu_type_nullable/up.sql new file mode 100644 index 0000000..f247f95 --- /dev/null +++ b/htyws_models/migrations/2022-01-14-145755_qumu_type_nullable/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +alter table qumu + alter column qumu_type drop not null; + diff --git a/htyws_models/migrations/2022-01-15-064023_modify_comment_in_piyueinfo_to_jsonb/down.sql b/htyws_models/migrations/2022-01-15-064023_modify_comment_in_piyueinfo_to_jsonb/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-01-15-064023_modify_comment_in_piyueinfo_to_jsonb/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-15-064023_modify_comment_in_piyueinfo_to_jsonb/up.sql b/htyws_models/migrations/2022-01-15-064023_modify_comment_in_piyueinfo_to_jsonb/up.sql new file mode 100644 index 0000000..fca8a1b --- /dev/null +++ b/htyws_models/migrations/2022-01-15-064023_modify_comment_in_piyueinfo_to_jsonb/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +alter table piyue_info alter column comment type jsonb using comment::jsonb; \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-15-075731_delete_content_column_in_piyue/down.sql b/htyws_models/migrations/2022-01-15-075731_delete_content_column_in_piyue/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-01-15-075731_delete_content_column_in_piyue/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-01-15-075731_delete_content_column_in_piyue/up.sql b/htyws_models/migrations/2022-01-15-075731_delete_content_column_in_piyue/up.sql new file mode 100644 index 0000000..4504e6a --- /dev/null +++ b/htyws_models/migrations/2022-01-15-075731_delete_content_column_in_piyue/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +alter table piyue drop column content; + diff --git a/htyws_models/migrations/2022-03-12-093256_add_created_at_to_jihua_qumu_section/down.sql b/htyws_models/migrations/2022-03-12-093256_add_created_at_to_jihua_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-03-12-093256_add_created_at_to_jihua_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-03-12-093256_add_created_at_to_jihua_qumu_section/up.sql b/htyws_models/migrations/2022-03-12-093256_add_created_at_to_jihua_qumu_section/up.sql new file mode 100644 index 0000000..ef81303 --- /dev/null +++ b/htyws_models/migrations/2022-03-12-093256_add_created_at_to_jihua_qumu_section/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table jihua_qumu_section + add created_at timestamp; + diff --git a/htyws_models/migrations/2022-03-16-132642_add_user_groups/down.sql b/htyws_models/migrations/2022-03-16-132642_add_user_groups/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-03-16-132642_add_user_groups/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-03-16-132642_add_user_groups/up.sql b/htyws_models/migrations/2022-03-16-132642_add_user_groups/up.sql new file mode 100644 index 0000000..8e57557 --- /dev/null +++ b/htyws_models/migrations/2022-03-16-132642_add_user_groups/up.sql @@ -0,0 +1,16 @@ +-- Your SQL goes here + +create table user_groups +( + id varchar not null + constraint user_groups_pk + primary key, + group_name varchar not null, + group_type varchar not null, + owner_id varchar not null, + group_members jsonb +); + +create unique index user_groups_id_uindex + on user_groups (id); + diff --git a/htyws_models/migrations/2022-03-18-061022_modify_daka_tbl/down.sql b/htyws_models/migrations/2022-03-18-061022_modify_daka_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-03-18-061022_modify_daka_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-03-18-061022_modify_daka_tbl/up.sql b/htyws_models/migrations/2022-03-18-061022_modify_daka_tbl/up.sql new file mode 100644 index 0000000..1f50e3a --- /dev/null +++ b/htyws_models/migrations/2022-03-18-061022_modify_daka_tbl/up.sql @@ -0,0 +1,23 @@ +-- Your SQL goes here + +alter table daka + drop column daka_date; + +alter table daka + add start_date timestamp not null; + +alter table daka + drop column student_id; + +alter table daka + rename column beizhu to "desc"; + +alter table daka + add duration_days int not null; + +alter table daka + add name varchar not null; + +alter table daka + add owner_id varchar not null; + diff --git a/htyws_models/migrations/2022-03-20-151854_add_daka_queue/down.sql b/htyws_models/migrations/2022-03-20-151854_add_daka_queue/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-03-20-151854_add_daka_queue/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-03-20-151854_add_daka_queue/up.sql b/htyws_models/migrations/2022-03-20-151854_add_daka_queue/up.sql new file mode 100644 index 0000000..0e7defa --- /dev/null +++ b/htyws_models/migrations/2022-03-20-151854_add_daka_queue/up.sql @@ -0,0 +1,16 @@ +-- Your SQL goes here + +create table daka_queue +( + id varchar not null + constraint daka_queue_pk + primary key, + qumu_section_queue jsonb, + daka_id varchar not null + constraint daka_queue_daka_id_fk + references daka +); + +create unique index daka_queue_id_uindex + on daka_queue (id); + diff --git a/htyws_models/migrations/2022-03-20-152315_add_group_id_to_daka_queue/down.sql b/htyws_models/migrations/2022-03-20-152315_add_group_id_to_daka_queue/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-03-20-152315_add_group_id_to_daka_queue/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-03-20-152315_add_group_id_to_daka_queue/up.sql b/htyws_models/migrations/2022-03-20-152315_add_group_id_to_daka_queue/up.sql new file mode 100644 index 0000000..a6ed7c3 --- /dev/null +++ b/htyws_models/migrations/2022-03-20-152315_add_group_id_to_daka_queue/up.sql @@ -0,0 +1,9 @@ +-- Your SQL goes here + +alter table daka_queue + add group_id varchar; + +alter table daka_queue + add constraint daka_queue_user_groups_id_fk + foreign key (group_id) references user_groups; + diff --git a/htyws_models/migrations/2022-03-22-110351_add_qumu_type_to_qumu_section/down.sql b/htyws_models/migrations/2022-03-22-110351_add_qumu_type_to_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-03-22-110351_add_qumu_type_to_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-03-22-110351_add_qumu_type_to_qumu_section/up.sql b/htyws_models/migrations/2022-03-22-110351_add_qumu_type_to_qumu_section/up.sql new file mode 100644 index 0000000..fb48bec --- /dev/null +++ b/htyws_models/migrations/2022-03-22-110351_add_qumu_type_to_qumu_section/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table qumu_section + add qumu_type varchar; + diff --git a/htyws_models/migrations/2022-05-02-033006_add_lianxi_count/down.sql b/htyws_models/migrations/2022-05-02-033006_add_lianxi_count/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-05-02-033006_add_lianxi_count/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-05-02-033006_add_lianxi_count/up.sql b/htyws_models/migrations/2022-05-02-033006_add_lianxi_count/up.sql new file mode 100644 index 0000000..9e729c6 --- /dev/null +++ b/htyws_models/migrations/2022-05-02-033006_add_lianxi_count/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table jihua + add lianxi_count int; + diff --git a/htyws_models/migrations/2022-05-07-070312_add_status_for_teacher_student/down.sql b/htyws_models/migrations/2022-05-07-070312_add_status_for_teacher_student/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-05-07-070312_add_status_for_teacher_student/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-05-07-070312_add_status_for_teacher_student/up.sql b/htyws_models/migrations/2022-05-07-070312_add_status_for_teacher_student/up.sql new file mode 100644 index 0000000..d4a2391 --- /dev/null +++ b/htyws_models/migrations/2022-05-07-070312_add_status_for_teacher_student/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +alter table teacher_student + add status varchar default 'Approved' not null; + diff --git a/htyws_models/migrations/2022-05-10-141321_add_qumu_name_to_qumu_section/down.sql b/htyws_models/migrations/2022-05-10-141321_add_qumu_name_to_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-05-10-141321_add_qumu_name_to_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-05-10-141321_add_qumu_name_to_qumu_section/up.sql b/htyws_models/migrations/2022-05-10-141321_add_qumu_name_to_qumu_section/up.sql new file mode 100644 index 0000000..4233489 --- /dev/null +++ b/htyws_models/migrations/2022-05-10-141321_add_qumu_name_to_qumu_section/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table qumu_section + add qumu_name varchar; + diff --git a/htyws_models/migrations/2022-05-20-113811_add_comments_tbl/down.sql b/htyws_models/migrations/2022-05-20-113811_add_comments_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-05-20-113811_add_comments_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-05-20-113811_add_comments_tbl/up.sql b/htyws_models/migrations/2022-05-20-113811_add_comments_tbl/up.sql new file mode 100644 index 0000000..3bcbbd9 --- /dev/null +++ b/htyws_models/migrations/2022-05-20-113811_add_comments_tbl/up.sql @@ -0,0 +1,19 @@ +-- Your SQL goes here + +create table comments +( + id varchar not null + constraint comments_pk + primary key, + ref_id varchar not null, + ref_type varchar not null, + parent_id varchar, + created_at timestamp not null, + created_by varchar not null, + creator_name varchar, + content jsonb +); + +create unique index comments_id_uindex + on comments (id); + diff --git a/htyws_models/migrations/2022-05-22-104845_add_created_at_to_qumu_section/down.sql b/htyws_models/migrations/2022-05-22-104845_add_created_at_to_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-05-22-104845_add_created_at_to_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-05-22-104845_add_created_at_to_qumu_section/up.sql b/htyws_models/migrations/2022-05-22-104845_add_created_at_to_qumu_section/up.sql new file mode 100644 index 0000000..8091028 --- /dev/null +++ b/htyws_models/migrations/2022-05-22-104845_add_created_at_to_qumu_section/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table qumu_section + add created_at timestamp; + diff --git a/htyws_models/migrations/2022-05-24-143614_add_hidden_for_qumu_section/down.sql b/htyws_models/migrations/2022-05-24-143614_add_hidden_for_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-05-24-143614_add_hidden_for_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-05-24-143614_add_hidden_for_qumu_section/up.sql b/htyws_models/migrations/2022-05-24-143614_add_hidden_for_qumu_section/up.sql new file mode 100644 index 0000000..0345035 --- /dev/null +++ b/htyws_models/migrations/2022-05-24-143614_add_hidden_for_qumu_section/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +alter table qumu_section + add hidden bool default false not null; \ No newline at end of file diff --git a/htyws_models/migrations/2022-05-28-140501_add_created_at_to_teacher_student_tbl/down.sql b/htyws_models/migrations/2022-05-28-140501_add_created_at_to_teacher_student_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-05-28-140501_add_created_at_to_teacher_student_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-05-28-140501_add_created_at_to_teacher_student_tbl/up.sql b/htyws_models/migrations/2022-05-28-140501_add_created_at_to_teacher_student_tbl/up.sql new file mode 100644 index 0000000..dffa4c0 --- /dev/null +++ b/htyws_models/migrations/2022-05-28-140501_add_created_at_to_teacher_student_tbl/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table teacher_student + add created_at timestamp; + diff --git a/htyws_models/migrations/2022-06-09-153148_rename_create_at_to_created_at_in_lianxi/down.sql b/htyws_models/migrations/2022-06-09-153148_rename_create_at_to_created_at_in_lianxi/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-06-09-153148_rename_create_at_to_created_at_in_lianxi/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-06-09-153148_rename_create_at_to_created_at_in_lianxi/up.sql b/htyws_models/migrations/2022-06-09-153148_rename_create_at_to_created_at_in_lianxi/up.sql new file mode 100644 index 0000000..1779001 --- /dev/null +++ b/htyws_models/migrations/2022-06-09-153148_rename_create_at_to_created_at_in_lianxi/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table lianxi + rename column create_at to created_at; + diff --git a/htyws_models/migrations/2022-06-09-153612_add_created_at_to_piyue_and_piyue_info/down.sql b/htyws_models/migrations/2022-06-09-153612_add_created_at_to_piyue_and_piyue_info/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-06-09-153612_add_created_at_to_piyue_and_piyue_info/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-06-09-153612_add_created_at_to_piyue_and_piyue_info/up.sql b/htyws_models/migrations/2022-06-09-153612_add_created_at_to_piyue_and_piyue_info/up.sql new file mode 100644 index 0000000..0d29198 --- /dev/null +++ b/htyws_models/migrations/2022-06-09-153612_add_created_at_to_piyue_and_piyue_info/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table piyue + add created_at timestamp; + +alter table piyue_info + add created_at timestamp; + diff --git a/htyws_models/migrations/2022-06-11-094923_add_teacher_name_to_piyue/down.sql b/htyws_models/migrations/2022-06-11-094923_add_teacher_name_to_piyue/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-06-11-094923_add_teacher_name_to_piyue/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-06-11-094923_add_teacher_name_to_piyue/up.sql b/htyws_models/migrations/2022-06-11-094923_add_teacher_name_to_piyue/up.sql new file mode 100644 index 0000000..f2d5e63 --- /dev/null +++ b/htyws_models/migrations/2022-06-11-094923_add_teacher_name_to_piyue/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table piyue + add teacher_name varchar; + diff --git a/htyws_models/migrations/2022-06-12-042118_remove_answer_and_comment_field/down.sql b/htyws_models/migrations/2022-06-12-042118_remove_answer_and_comment_field/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-06-12-042118_remove_answer_and_comment_field/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-06-12-042118_remove_answer_and_comment_field/up.sql b/htyws_models/migrations/2022-06-12-042118_remove_answer_and_comment_field/up.sql new file mode 100644 index 0000000..c1a194c --- /dev/null +++ b/htyws_models/migrations/2022-06-12-042118_remove_answer_and_comment_field/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table piyue + drop column answer; + +alter table piyue_info + drop column comment; + diff --git a/htyws_models/migrations/2022-06-20-142804_add_is_yanqi_to_jihua_tbl/down.sql b/htyws_models/migrations/2022-06-20-142804_add_is_yanqi_to_jihua_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-06-20-142804_add_is_yanqi_to_jihua_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-06-20-142804_add_is_yanqi_to_jihua_tbl/up.sql b/htyws_models/migrations/2022-06-20-142804_add_is_yanqi_to_jihua_tbl/up.sql new file mode 100644 index 0000000..5282d1a --- /dev/null +++ b/htyws_models/migrations/2022-06-20-142804_add_is_yanqi_to_jihua_tbl/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table jihua + add is_yanqi bool; + diff --git a/htyws_models/migrations/2022-06-21-101424_add_has_piyue_to_lianxi_tbl/down.sql b/htyws_models/migrations/2022-06-21-101424_add_has_piyue_to_lianxi_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-06-21-101424_add_has_piyue_to_lianxi_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-06-21-101424_add_has_piyue_to_lianxi_tbl/up.sql b/htyws_models/migrations/2022-06-21-101424_add_has_piyue_to_lianxi_tbl/up.sql new file mode 100644 index 0000000..e4ec1de --- /dev/null +++ b/htyws_models/migrations/2022-06-21-101424_add_has_piyue_to_lianxi_tbl/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table lianxi + add has_piyue bool; + diff --git a/htyws_models/migrations/2022-06-24-090146_add_is_delete_to_qumu_section/down.sql b/htyws_models/migrations/2022-06-24-090146_add_is_delete_to_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-06-24-090146_add_is_delete_to_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-06-24-090146_add_is_delete_to_qumu_section/up.sql b/htyws_models/migrations/2022-06-24-090146_add_is_delete_to_qumu_section/up.sql new file mode 100644 index 0000000..faa4a75 --- /dev/null +++ b/htyws_models/migrations/2022-06-24-090146_add_is_delete_to_qumu_section/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + +alter table qumu_section + add is_delete boolean default false; + +update qumu_section set is_delete='false'; diff --git a/htyws_models/migrations/2022-06-24-091457_add_is_delete_to_jihua_qumu_section/down.sql b/htyws_models/migrations/2022-06-24-091457_add_is_delete_to_jihua_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-06-24-091457_add_is_delete_to_jihua_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-06-24-091457_add_is_delete_to_jihua_qumu_section/up.sql b/htyws_models/migrations/2022-06-24-091457_add_is_delete_to_jihua_qumu_section/up.sql new file mode 100644 index 0000000..6ed28f0 --- /dev/null +++ b/htyws_models/migrations/2022-06-24-091457_add_is_delete_to_jihua_qumu_section/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + +alter table jihua_qumu_section + add is_delete boolean default false; + +update jihua_qumu_section set is_delete='false'; \ No newline at end of file diff --git a/htyws_models/migrations/2022-06-30-153201_add_parent_id_to_jihua/down.sql b/htyws_models/migrations/2022-06-30-153201_add_parent_id_to_jihua/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-06-30-153201_add_parent_id_to_jihua/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-06-30-153201_add_parent_id_to_jihua/up.sql b/htyws_models/migrations/2022-06-30-153201_add_parent_id_to_jihua/up.sql new file mode 100644 index 0000000..a8e451e --- /dev/null +++ b/htyws_models/migrations/2022-06-30-153201_add_parent_id_to_jihua/up.sql @@ -0,0 +1,9 @@ +-- Your SQL goes here + +alter table jihua + add parent_id varchar; + +alter table jihua + add constraint jihua_jihua_id_fk + foreign key (parent_id) references jihua; + diff --git a/htyws_models/migrations/2022-07-01-093915_add_is_delete_to_jihua/down.sql b/htyws_models/migrations/2022-07-01-093915_add_is_delete_to_jihua/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-07-01-093915_add_is_delete_to_jihua/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-01-093915_add_is_delete_to_jihua/up.sql b/htyws_models/migrations/2022-07-01-093915_add_is_delete_to_jihua/up.sql new file mode 100644 index 0000000..08070af --- /dev/null +++ b/htyws_models/migrations/2022-07-01-093915_add_is_delete_to_jihua/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + +alter table jihua + add is_delete boolean; + +update jihua set is_delete='false'; \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-05-055730_add_group_id_to_daka/down.sql b/htyws_models/migrations/2022-07-05-055730_add_group_id_to_daka/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-07-05-055730_add_group_id_to_daka/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-05-055730_add_group_id_to_daka/up.sql b/htyws_models/migrations/2022-07-05-055730_add_group_id_to_daka/up.sql new file mode 100644 index 0000000..655679d --- /dev/null +++ b/htyws_models/migrations/2022-07-05-055730_add_group_id_to_daka/up.sql @@ -0,0 +1,19 @@ +-- Your SQL goes here +alter table daka + add created_by varchar not null; + +alter table daka + rename column owner_id to created_at; + +alter table daka + alter column created_at type timestamp using created_at::timestamp; + +alter table daka + add teacher_id varchar not null; + +alter table daka + add group_id varchar not null; + +alter table daka + add is_delete bool not null; + diff --git a/htyws_models/migrations/2022-07-05-060330_add_is_delete_to_daka_qumu_section/down.sql b/htyws_models/migrations/2022-07-05-060330_add_is_delete_to_daka_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-07-05-060330_add_is_delete_to_daka_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-05-060330_add_is_delete_to_daka_qumu_section/up.sql b/htyws_models/migrations/2022-07-05-060330_add_is_delete_to_daka_qumu_section/up.sql new file mode 100644 index 0000000..9868d31 --- /dev/null +++ b/htyws_models/migrations/2022-07-05-060330_add_is_delete_to_daka_qumu_section/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +alter table daka_qumu_section + add is_delete bool not null; + diff --git a/htyws_models/migrations/2022-07-05-061247_drop_user_groups_tbl/down.sql b/htyws_models/migrations/2022-07-05-061247_drop_user_groups_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-07-05-061247_drop_user_groups_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-05-061247_drop_user_groups_tbl/up.sql b/htyws_models/migrations/2022-07-05-061247_drop_user_groups_tbl/up.sql new file mode 100644 index 0000000..ca73750 --- /dev/null +++ b/htyws_models/migrations/2022-07-05-061247_drop_user_groups_tbl/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here +drop table daka_queue; + + +drop table user_groups; + diff --git a/htyws_models/migrations/2022-07-05-061923_add_fields_to_daka/down.sql b/htyws_models/migrations/2022-07-05-061923_add_fields_to_daka/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-07-05-061923_add_fields_to_daka/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-05-061923_add_fields_to_daka/up.sql b/htyws_models/migrations/2022-07-05-061923_add_fields_to_daka/up.sql new file mode 100644 index 0000000..8e9b3e3 --- /dev/null +++ b/htyws_models/migrations/2022-07-05-061923_add_fields_to_daka/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table daka + add teacher_name varchar; + +alter table daka + add group_name varchar; + diff --git a/htyws_models/migrations/2022-07-09-150124_add_is_delete_to_piyue/down.sql b/htyws_models/migrations/2022-07-09-150124_add_is_delete_to_piyue/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-07-09-150124_add_is_delete_to_piyue/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-09-150124_add_is_delete_to_piyue/up.sql b/htyws_models/migrations/2022-07-09-150124_add_is_delete_to_piyue/up.sql new file mode 100644 index 0000000..009e4eb --- /dev/null +++ b/htyws_models/migrations/2022-07-09-150124_add_is_delete_to_piyue/up.sql @@ -0,0 +1,17 @@ +-- Your SQL goes here +alter table piyue + add is_delete bool; + +alter table comments + add is_delete bool; + +alter table lianxi + add is_delete bool; + +alter table piyue_info + add is_delete bool; + + + + + diff --git a/htyws_models/migrations/2022-07-09-161305_update_is_delete/down.sql b/htyws_models/migrations/2022-07-09-161305_update_is_delete/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-07-09-161305_update_is_delete/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-09-161305_update_is_delete/up.sql b/htyws_models/migrations/2022-07-09-161305_update_is_delete/up.sql new file mode 100644 index 0000000..a574307 --- /dev/null +++ b/htyws_models/migrations/2022-07-09-161305_update_is_delete/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + +update lianxi set is_delete='false'; +update piyue set is_delete='false'; +update comments set is_delete='false'; +update piyue_info set is_delete='false'; \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-10-153006_add_comment_type_and_status/down.sql b/htyws_models/migrations/2022-07-10-153006_add_comment_type_and_status/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-07-10-153006_add_comment_type_and_status/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-10-153006_add_comment_type_and_status/up.sql b/htyws_models/migrations/2022-07-10-153006_add_comment_type_and_status/up.sql new file mode 100644 index 0000000..d9e8b23 --- /dev/null +++ b/htyws_models/migrations/2022-07-10-153006_add_comment_type_and_status/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table comments + add comment_type varchar; + +alter table comments + add comment_status varchar; + diff --git a/htyws_models/migrations/2022-07-10-155857_add_is_delete_to_qumu/down.sql b/htyws_models/migrations/2022-07-10-155857_add_is_delete_to_qumu/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-07-10-155857_add_is_delete_to_qumu/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-10-155857_add_is_delete_to_qumu/up.sql b/htyws_models/migrations/2022-07-10-155857_add_is_delete_to_qumu/up.sql new file mode 100644 index 0000000..5f1b0f8 --- /dev/null +++ b/htyws_models/migrations/2022-07-10-155857_add_is_delete_to_qumu/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +alter table qumu + add is_delete bool; + diff --git a/htyws_models/migrations/2022-07-11-052137_add_default_is_delete/down.sql b/htyws_models/migrations/2022-07-11-052137_add_default_is_delete/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-07-11-052137_add_default_is_delete/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-11-052137_add_default_is_delete/up.sql b/htyws_models/migrations/2022-07-11-052137_add_default_is_delete/up.sql new file mode 100644 index 0000000..6e9b7f7 --- /dev/null +++ b/htyws_models/migrations/2022-07-11-052137_add_default_is_delete/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here + +update qumu set is_delete='false'; \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-19-161045_add_fields_to_ref_resources/down.sql b/htyws_models/migrations/2022-07-19-161045_add_fields_to_ref_resources/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-07-19-161045_add_fields_to_ref_resources/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-07-19-161045_add_fields_to_ref_resources/up.sql b/htyws_models/migrations/2022-07-19-161045_add_fields_to_ref_resources/up.sql new file mode 100644 index 0000000..e2a57f3 --- /dev/null +++ b/htyws_models/migrations/2022-07-19-161045_add_fields_to_ref_resources/up.sql @@ -0,0 +1,12 @@ +-- Your SQL goes here + +alter table ref_resources + add ref_name varchar; + +alter table ref_resources + add ref_desc varchar; + +alter table ref_resources + add meta jsonb; + + diff --git a/htyws_models/migrations/2022-08-01-153302_add_scores_tbl/down.sql b/htyws_models/migrations/2022-08-01-153302_add_scores_tbl/down.sql new file mode 100644 index 0000000..5f34ca4 --- /dev/null +++ b/htyws_models/migrations/2022-08-01-153302_add_scores_tbl/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` + +drop table scores \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-01-153302_add_scores_tbl/up.sql b/htyws_models/migrations/2022-08-01-153302_add_scores_tbl/up.sql new file mode 100644 index 0000000..f8ddf9a --- /dev/null +++ b/htyws_models/migrations/2022-08-01-153302_add_scores_tbl/up.sql @@ -0,0 +1,21 @@ +-- Your SQL goes here + +create table scores +( + id varchar not null, + ref_id varchar not null, + ref_type varchar not null, + score_val int not null, + score_from varchar, + created_at timestamp, + created_by timestamp, + meta jsonb +); + +create unique index scores_id_uindex + on scores (id); + +alter table scores + add constraint scores_pk + primary key (id); + diff --git a/htyws_models/migrations/2022-08-02-194353_add_task_fields/down.sql b/htyws_models/migrations/2022-08-02-194353_add_task_fields/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-02-194353_add_task_fields/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-02-194353_add_task_fields/up.sql b/htyws_models/migrations/2022-08-02-194353_add_task_fields/up.sql new file mode 100644 index 0000000..4df72e9 --- /dev/null +++ b/htyws_models/migrations/2022-08-02-194353_add_task_fields/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table ref_resources + add task jsonb; + +alter table scores + add task jsonb; + diff --git a/htyws_models/migrations/2022-08-02-194608_rename_task_name/down.sql b/htyws_models/migrations/2022-08-02-194608_rename_task_name/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-02-194608_rename_task_name/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-02-194608_rename_task_name/up.sql b/htyws_models/migrations/2022-08-02-194608_rename_task_name/up.sql new file mode 100644 index 0000000..2ac19a3 --- /dev/null +++ b/htyws_models/migrations/2022-08-02-194608_rename_task_name/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table scores + rename column task to tasks; + +alter table ref_resources + rename column task to tasks; + diff --git a/htyws_models/migrations/2022-08-07-143009_add_kecheng_tbl/down.sql b/htyws_models/migrations/2022-08-07-143009_add_kecheng_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-07-143009_add_kecheng_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-07-143009_add_kecheng_tbl/up.sql b/htyws_models/migrations/2022-08-07-143009_add_kecheng_tbl/up.sql new file mode 100644 index 0000000..a2ecce8 --- /dev/null +++ b/htyws_models/migrations/2022-08-07-143009_add_kecheng_tbl/up.sql @@ -0,0 +1,22 @@ +-- Your SQL goes here + +create table kecheng +( + id varchar not null, + group_id varchar, + kecheng_name varchar not null, + kecheng_desc varchar, + teacher_id varchar not null, + student_id varchar not null, + start_from timestamp not null, + end_by timestamp not null, + kecheng_status varchar not null +); + +create unique index kecheng_id_uindex + on kecheng (id); + +alter table kecheng + add constraint kecheng_pk + primary key (id); + diff --git a/htyws_models/migrations/2022-08-07-144322_add_kecheng_teacher/down.sql b/htyws_models/migrations/2022-08-07-144322_add_kecheng_teacher/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-07-144322_add_kecheng_teacher/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-07-144322_add_kecheng_teacher/up.sql b/htyws_models/migrations/2022-08-07-144322_add_kecheng_teacher/up.sql new file mode 100644 index 0000000..ee9b802 --- /dev/null +++ b/htyws_models/migrations/2022-08-07-144322_add_kecheng_teacher/up.sql @@ -0,0 +1,20 @@ +-- Your SQL goes here + +create table kecheng_teacher +( + id varchar not null, + teacher_id varchar not null, + kecheng_name varchar not null, + kecheng_status varchar not null, + kecheng_desc varchar, + start_from timestamp not null, + end_by timestamp not null +); + +create unique index kecheng_teacher_id_uindex + on kecheng_teacher (id); + +alter table kecheng_teacher + add constraint kecheng_teacher_pk + primary key (id); + diff --git a/htyws_models/migrations/2022-08-07-144418_refactor_kecheng_tbl/down.sql b/htyws_models/migrations/2022-08-07-144418_refactor_kecheng_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-07-144418_refactor_kecheng_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-07-144418_refactor_kecheng_tbl/up.sql b/htyws_models/migrations/2022-08-07-144418_refactor_kecheng_tbl/up.sql new file mode 100644 index 0000000..09d8fda --- /dev/null +++ b/htyws_models/migrations/2022-08-07-144418_refactor_kecheng_tbl/up.sql @@ -0,0 +1,18 @@ +-- Your SQL goes here + +alter table kecheng + rename column group_id to kecheng_teacher_id; + +alter table kecheng + alter column kecheng_teacher_id set not null; + +alter table kecheng + drop column teacher_id; + +alter table kecheng + rename to kecheng_student; + +alter table kecheng_student + add constraint kecheng_student_kecheng_teacher_id_fk + foreign key (kecheng_teacher_id) references kecheng_teacher; + diff --git a/htyws_models/migrations/2022-08-08-054117_add_is_shifan_to_ref_resources/down.sql b/htyws_models/migrations/2022-08-08-054117_add_is_shifan_to_ref_resources/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-08-054117_add_is_shifan_to_ref_resources/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-08-054117_add_is_shifan_to_ref_resources/up.sql b/htyws_models/migrations/2022-08-08-054117_add_is_shifan_to_ref_resources/up.sql new file mode 100644 index 0000000..dbcf320 --- /dev/null +++ b/htyws_models/migrations/2022-08-08-054117_add_is_shifan_to_ref_resources/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + + +alter table ref_resources + add is_shifan boolean; + diff --git a/htyws_models/migrations/2022-08-08-073401_set_default_is_shifan/down.sql b/htyws_models/migrations/2022-08-08-073401_set_default_is_shifan/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-08-073401_set_default_is_shifan/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-08-073401_set_default_is_shifan/up.sql b/htyws_models/migrations/2022-08-08-073401_set_default_is_shifan/up.sql new file mode 100644 index 0000000..3688569 --- /dev/null +++ b/htyws_models/migrations/2022-08-08-073401_set_default_is_shifan/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here + +update ref_resources set is_shifan=false; diff --git a/htyws_models/migrations/2022-08-08-140130_change_scores_table_created_by_and_score_val/down.sql b/htyws_models/migrations/2022-08-08-140130_change_scores_table_created_by_and_score_val/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-08-140130_change_scores_table_created_by_and_score_val/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-08-140130_change_scores_table_created_by_and_score_val/up.sql b/htyws_models/migrations/2022-08-08-140130_change_scores_table_created_by_and_score_val/up.sql new file mode 100644 index 0000000..45175e7 --- /dev/null +++ b/htyws_models/migrations/2022-08-08-140130_change_scores_table_created_by_and_score_val/up.sql @@ -0,0 +1,2 @@ +alter table scores alter column score_val type float4 using score_val::float4; +alter table scores alter column created_by type varchar using created_by::varchar;-- Your SQL goes here \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-09-191843_score_val_can_be_null/down.sql b/htyws_models/migrations/2022-08-09-191843_score_val_can_be_null/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-09-191843_score_val_can_be_null/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-09-191843_score_val_can_be_null/up.sql b/htyws_models/migrations/2022-08-09-191843_score_val_can_be_null/up.sql new file mode 100644 index 0000000..0822772 --- /dev/null +++ b/htyws_models/migrations/2022-08-09-191843_score_val_can_be_null/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here + +alter table scores + alter column score_val drop not null; diff --git a/htyws_models/migrations/2022-08-14-143407_add_qupu_text_to_qumu_section/down.sql b/htyws_models/migrations/2022-08-14-143407_add_qupu_text_to_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-14-143407_add_qupu_text_to_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-14-143407_add_qupu_text_to_qumu_section/up.sql b/htyws_models/migrations/2022-08-14-143407_add_qupu_text_to_qumu_section/up.sql new file mode 100644 index 0000000..d6c49b8 --- /dev/null +++ b/htyws_models/migrations/2022-08-14-143407_add_qupu_text_to_qumu_section/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table qumu_section + add qupu_text text; + diff --git a/htyws_models/migrations/2022-08-21-063222_remove_qumu_section_fields/down.sql b/htyws_models/migrations/2022-08-21-063222_remove_qumu_section_fields/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-21-063222_remove_qumu_section_fields/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-21-063222_remove_qumu_section_fields/up.sql b/htyws_models/migrations/2022-08-21-063222_remove_qumu_section_fields/up.sql new file mode 100644 index 0000000..8582f70 --- /dev/null +++ b/htyws_models/migrations/2022-08-21-063222_remove_qumu_section_fields/up.sql @@ -0,0 +1,17 @@ +-- Your SQL goes here + +alter table qumu_section + drop column qupu_id; + +alter table qumu_section + drop column qupu_url; + +alter table qumu_section + drop column shifan_id; + +alter table qumu_section + drop column shifan_url; + +alter table qumu_section + drop column task; + diff --git a/htyws_models/migrations/2022-08-21-070500_hty_resource_id_can_be_null/down.sql b/htyws_models/migrations/2022-08-21-070500_hty_resource_id_can_be_null/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-21-070500_hty_resource_id_can_be_null/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-21-070500_hty_resource_id_can_be_null/up.sql b/htyws_models/migrations/2022-08-21-070500_hty_resource_id_can_be_null/up.sql new file mode 100644 index 0000000..1566ef6 --- /dev/null +++ b/htyws_models/migrations/2022-08-21-070500_hty_resource_id_can_be_null/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table ref_resources + alter column hty_resource_id drop not null; + diff --git a/htyws_models/migrations/2022-08-21-070606_ref_resource_fields_can_be_nullo/down.sql b/htyws_models/migrations/2022-08-21-070606_ref_resource_fields_can_be_nullo/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-21-070606_ref_resource_fields_can_be_nullo/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-21-070606_ref_resource_fields_can_be_nullo/up.sql b/htyws_models/migrations/2022-08-21-070606_ref_resource_fields_can_be_nullo/up.sql new file mode 100644 index 0000000..85c78e7 --- /dev/null +++ b/htyws_models/migrations/2022-08-21-070606_ref_resource_fields_can_be_nullo/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table ref_resources + alter column ref_id drop not null; + +alter table ref_resources + alter column ref_type drop not null; + diff --git a/htyws_models/migrations/2022-08-24-062912_add_is_repeat_to_kecheng/down.sql b/htyws_models/migrations/2022-08-24-062912_add_is_repeat_to_kecheng/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-24-062912_add_is_repeat_to_kecheng/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-24-062912_add_is_repeat_to_kecheng/up.sql b/htyws_models/migrations/2022-08-24-062912_add_is_repeat_to_kecheng/up.sql new file mode 100644 index 0000000..c872fbc --- /dev/null +++ b/htyws_models/migrations/2022-08-24-062912_add_is_repeat_to_kecheng/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table kecheng_student + add is_repeat boolean; + +alter table kecheng_teacher + add is_repeat boolean; + diff --git a/htyws_models/migrations/2022-08-30-142946_add_qumu_text/down.sql b/htyws_models/migrations/2022-08-30-142946_add_qumu_text/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-30-142946_add_qumu_text/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-30-142946_add_qumu_text/up.sql b/htyws_models/migrations/2022-08-30-142946_add_qumu_text/up.sql new file mode 100644 index 0000000..aaef718 --- /dev/null +++ b/htyws_models/migrations/2022-08-30-142946_add_qumu_text/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + +alter table qumu_section + add qumu_text text; + + diff --git a/htyws_models/migrations/2022-08-30-143057_rename_qupu_xml/down.sql b/htyws_models/migrations/2022-08-30-143057_rename_qupu_xml/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-08-30-143057_rename_qupu_xml/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-08-30-143057_rename_qupu_xml/up.sql b/htyws_models/migrations/2022-08-30-143057_rename_qupu_xml/up.sql new file mode 100644 index 0000000..2277f98 --- /dev/null +++ b/htyws_models/migrations/2022-08-30-143057_rename_qupu_xml/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table qumu_section + rename column qupu_text to qupu_xml; + diff --git a/htyws_models/migrations/2022-09-03-150303_add_multi_scores_to_scores_tbl/down.sql b/htyws_models/migrations/2022-09-03-150303_add_multi_scores_to_scores_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-03-150303_add_multi_scores_to_scores_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-03-150303_add_multi_scores_to_scores_tbl/up.sql b/htyws_models/migrations/2022-09-03-150303_add_multi_scores_to_scores_tbl/up.sql new file mode 100644 index 0000000..a9f0009 --- /dev/null +++ b/htyws_models/migrations/2022-09-03-150303_add_multi_scores_to_scores_tbl/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table scores + add multi_scores jsonb; + diff --git a/htyws_models/migrations/2022-09-04-161257_add_resource_note_tbl/down.sql b/htyws_models/migrations/2022-09-04-161257_add_resource_note_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-04-161257_add_resource_note_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-04-161257_add_resource_note_tbl/up.sql b/htyws_models/migrations/2022-09-04-161257_add_resource_note_tbl/up.sql new file mode 100644 index 0000000..b1d0e11 --- /dev/null +++ b/htyws_models/migrations/2022-09-04-161257_add_resource_note_tbl/up.sql @@ -0,0 +1,14 @@ +-- Your SQL goes here + +create table resource_note +( + id varchar not null + primary key + unique, + created_at timestamp, + created_by varchar, + serial_no varchar, + is_delete boolean, + resource_pos jsonb +); + diff --git a/htyws_models/migrations/2022-09-04-161431_add_children_to_ref_resources/down.sql b/htyws_models/migrations/2022-09-04-161431_add_children_to_ref_resources/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-04-161431_add_children_to_ref_resources/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-04-161431_add_children_to_ref_resources/up.sql b/htyws_models/migrations/2022-09-04-161431_add_children_to_ref_resources/up.sql new file mode 100644 index 0000000..0ed760b --- /dev/null +++ b/htyws_models/migrations/2022-09-04-161431_add_children_to_ref_resources/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table ref_resources + add children jsonb; + diff --git a/htyws_models/migrations/2022-09-04-165106_add_ref_resource_group_tbl/down.sql b/htyws_models/migrations/2022-09-04-165106_add_ref_resource_group_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-04-165106_add_ref_resource_group_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-04-165106_add_ref_resource_group_tbl/up.sql b/htyws_models/migrations/2022-09-04-165106_add_ref_resource_group_tbl/up.sql new file mode 100644 index 0000000..1277015 --- /dev/null +++ b/htyws_models/migrations/2022-09-04-165106_add_ref_resource_group_tbl/up.sql @@ -0,0 +1,16 @@ +-- Your SQL goes here + +create table resource_note_group +( + id varchar not null + primary key + unique, + ref_id varchar not null, + ref_type varchar not null, + ref_resource_id varchar not null + constraint resource_note_group_ref_resources_id_fk + references ref_resources, + resource_note_ids jsonb, + meta jsonb +); + diff --git a/htyws_models/migrations/2022-09-04-170511_refactor_ref_resource_group/down.sql b/htyws_models/migrations/2022-09-04-170511_refactor_ref_resource_group/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-04-170511_refactor_ref_resource_group/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-04-170511_refactor_ref_resource_group/up.sql b/htyws_models/migrations/2022-09-04-170511_refactor_ref_resource_group/up.sql new file mode 100644 index 0000000..64cb36e --- /dev/null +++ b/htyws_models/migrations/2022-09-04-170511_refactor_ref_resource_group/up.sql @@ -0,0 +1,13 @@ +-- Your SQL goes here +alter table resource_note_group + drop constraint resource_note_group_ref_resources_id_fk; + +alter table resource_note_group + rename column ref_resource_id to ref_resource_ids; + +alter table resource_note_group + alter column ref_resource_ids type jsonb using ref_resource_ids::jsonb; + +alter table resource_note_group + alter column ref_resource_ids drop not null; + diff --git a/htyws_models/migrations/2022-09-04-170600_delete_children_from_ref_resources/down.sql b/htyws_models/migrations/2022-09-04-170600_delete_children_from_ref_resources/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-04-170600_delete_children_from_ref_resources/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-04-170600_delete_children_from_ref_resources/up.sql b/htyws_models/migrations/2022-09-04-170600_delete_children_from_ref_resources/up.sql new file mode 100644 index 0000000..360cb4a --- /dev/null +++ b/htyws_models/migrations/2022-09-04-170600_delete_children_from_ref_resources/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table ref_resources + drop column children; + diff --git a/htyws_models/migrations/2022-09-04-172836_add_is_delete_to_resource_group/down.sql b/htyws_models/migrations/2022-09-04-172836_add_is_delete_to_resource_group/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-04-172836_add_is_delete_to_resource_group/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-04-172836_add_is_delete_to_resource_group/up.sql b/htyws_models/migrations/2022-09-04-172836_add_is_delete_to_resource_group/up.sql new file mode 100644 index 0000000..68f52cc --- /dev/null +++ b/htyws_models/migrations/2022-09-04-172836_add_is_delete_to_resource_group/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table resource_note_group + add is_delete boolean not null; + diff --git a/htyws_models/migrations/2022-09-05-144931_add_created_at_to_resource_note_group/down.sql b/htyws_models/migrations/2022-09-05-144931_add_created_at_to_resource_note_group/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-05-144931_add_created_at_to_resource_note_group/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-05-144931_add_created_at_to_resource_note_group/up.sql b/htyws_models/migrations/2022-09-05-144931_add_created_at_to_resource_note_group/up.sql new file mode 100644 index 0000000..6f53430 --- /dev/null +++ b/htyws_models/migrations/2022-09-05-144931_add_created_at_to_resource_note_group/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table resource_note_group + add created_at timestamp; + +alter table resource_note_group + add created_by varchar; + diff --git a/htyws_models/migrations/2022-09-08-154816_add_lianxi_count_to_daka/down.sql b/htyws_models/migrations/2022-09-08-154816_add_lianxi_count_to_daka/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-08-154816_add_lianxi_count_to_daka/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-08-154816_add_lianxi_count_to_daka/up.sql b/htyws_models/migrations/2022-09-08-154816_add_lianxi_count_to_daka/up.sql new file mode 100644 index 0000000..52d6a9d --- /dev/null +++ b/htyws_models/migrations/2022-09-08-154816_add_lianxi_count_to_daka/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table daka + add lianxi_count integer; + diff --git a/htyws_models/migrations/2022-09-08-190328_add_created_by_to_lianxi/down.sql b/htyws_models/migrations/2022-09-08-190328_add_created_by_to_lianxi/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-08-190328_add_created_by_to_lianxi/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-08-190328_add_created_by_to_lianxi/up.sql b/htyws_models/migrations/2022-09-08-190328_add_created_by_to_lianxi/up.sql new file mode 100644 index 0000000..bf32b80 --- /dev/null +++ b/htyws_models/migrations/2022-09-08-190328_add_created_by_to_lianxi/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table lianxi + add created_by varchar; + +alter table lianxi + add creator_name varchar; + diff --git a/htyws_models/migrations/2022-09-25-154549_add_duration_to_kecheng/down.sql b/htyws_models/migrations/2022-09-25-154549_add_duration_to_kecheng/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-25-154549_add_duration_to_kecheng/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-25-154549_add_duration_to_kecheng/up.sql b/htyws_models/migrations/2022-09-25-154549_add_duration_to_kecheng/up.sql new file mode 100644 index 0000000..ba0d180 --- /dev/null +++ b/htyws_models/migrations/2022-09-25-154549_add_duration_to_kecheng/up.sql @@ -0,0 +1,12 @@ +-- Your SQL goes here + +alter table kecheng_teacher + add duration integer; + +comment on column kecheng_teacher.duration is '课程时长(分钟)'; + +alter table kecheng_student + add duration integer; + +comment on column kecheng_student.duration is '课程时长(分钟)'; + diff --git a/htyws_models/migrations/2022-09-25-155557_add_root_id_to_kecheng/down.sql b/htyws_models/migrations/2022-09-25-155557_add_root_id_to_kecheng/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-25-155557_add_root_id_to_kecheng/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-25-155557_add_root_id_to_kecheng/up.sql b/htyws_models/migrations/2022-09-25-155557_add_root_id_to_kecheng/up.sql new file mode 100644 index 0000000..ebf7e9a --- /dev/null +++ b/htyws_models/migrations/2022-09-25-155557_add_root_id_to_kecheng/up.sql @@ -0,0 +1,16 @@ +-- Your SQL goes here + +alter table kecheng_teacher + add root_id varchar not null; + +alter table kecheng_teacher + add constraint kecheng_teacher_kecheng_teacher_id_fk + foreign key (root_id) references kecheng_teacher; + + +alter table kecheng_student + add root_id varchar not null; + +alter table kecheng_student + add constraint kecheng_student_kecheng_student_id_fk + foreign key (root_id) references kecheng_student; diff --git a/htyws_models/migrations/2022-09-25-161509_add_kecheng_repeat/down.sql b/htyws_models/migrations/2022-09-25-161509_add_kecheng_repeat/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-25-161509_add_kecheng_repeat/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-25-161509_add_kecheng_repeat/up.sql b/htyws_models/migrations/2022-09-25-161509_add_kecheng_repeat/up.sql new file mode 100644 index 0000000..b0c6cec --- /dev/null +++ b/htyws_models/migrations/2022-09-25-161509_add_kecheng_repeat/up.sql @@ -0,0 +1,22 @@ +-- Your SQL goes here +create table kecheng_repeat +( + id varchar not null + primary key + unique, + kecheng_root_id varchar not null, + kecheng_id varchar not null, + kecheng_type varchar, + start_from timestamp, + end_by timestamp, + repeat_start timestamp +); + +comment on column kecheng_repeat.kecheng_type is 'STUDENT或者TEACHER'; + +comment on column kecheng_repeat.start_from is '课程当天开始时间'; + +comment on column kecheng_repeat.end_by is '课程的当天结束时间'; + +comment on column kecheng_repeat.repeat_start is '不需要`repeat_end`了,因为同一组`kecheng_root_id`的数据按照`repeat_start`时间排列,后一个就是前一个的end时间。'; + diff --git a/htyws_models/migrations/2022-09-25-161948_delete_is_repeat_from_kecheng/down.sql b/htyws_models/migrations/2022-09-25-161948_delete_is_repeat_from_kecheng/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-09-25-161948_delete_is_repeat_from_kecheng/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-09-25-161948_delete_is_repeat_from_kecheng/up.sql b/htyws_models/migrations/2022-09-25-161948_delete_is_repeat_from_kecheng/up.sql new file mode 100644 index 0000000..a44ed9f --- /dev/null +++ b/htyws_models/migrations/2022-09-25-161948_delete_is_repeat_from_kecheng/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table kecheng_student + drop column is_repeat; + +alter table kecheng_teacher + drop column is_repeat; + diff --git a/htyws_models/migrations/2022-10-06-154048_add_has_piyue_to_comments/down.sql b/htyws_models/migrations/2022-10-06-154048_add_has_piyue_to_comments/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-06-154048_add_has_piyue_to_comments/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-06-154048_add_has_piyue_to_comments/up.sql b/htyws_models/migrations/2022-10-06-154048_add_has_piyue_to_comments/up.sql new file mode 100644 index 0000000..a29ffc1 --- /dev/null +++ b/htyws_models/migrations/2022-10-06-154048_add_has_piyue_to_comments/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes + +alter table comments + add has_piyue boolean; + diff --git a/htyws_models/migrations/2022-10-15-183337_add_has_score_to_comments/down.sql b/htyws_models/migrations/2022-10-15-183337_add_has_score_to_comments/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-15-183337_add_has_score_to_comments/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-15-183337_add_has_score_to_comments/up.sql b/htyws_models/migrations/2022-10-15-183337_add_has_score_to_comments/up.sql new file mode 100644 index 0000000..5e3026f --- /dev/null +++ b/htyws_models/migrations/2022-10-15-183337_add_has_score_to_comments/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table comments + add has_score boolean; + diff --git a/htyws_models/migrations/2022-10-16-155152_refactor_kecheng_tbl/down.sql b/htyws_models/migrations/2022-10-16-155152_refactor_kecheng_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-16-155152_refactor_kecheng_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-16-155152_refactor_kecheng_tbl/up.sql b/htyws_models/migrations/2022-10-16-155152_refactor_kecheng_tbl/up.sql new file mode 100644 index 0000000..93f5ef8 --- /dev/null +++ b/htyws_models/migrations/2022-10-16-155152_refactor_kecheng_tbl/up.sql @@ -0,0 +1,10 @@ +-- Your SQL goes here + +alter table kecheng_teacher + drop column teacher_id; + +alter table kecheng_teacher + rename to kecheng; + +drop table kecheng_student; + diff --git a/htyws_models/migrations/2022-10-16-155534_create_kecheng_users_tbl/down.sql b/htyws_models/migrations/2022-10-16-155534_create_kecheng_users_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-16-155534_create_kecheng_users_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-16-155534_create_kecheng_users_tbl/up.sql b/htyws_models/migrations/2022-10-16-155534_create_kecheng_users_tbl/up.sql new file mode 100644 index 0000000..19f8efc --- /dev/null +++ b/htyws_models/migrations/2022-10-16-155534_create_kecheng_users_tbl/up.sql @@ -0,0 +1,14 @@ +-- Your SQL goes here + +create table kecheng_users +( + id varchar not null + primary key + unique, + user_id varchar not null, + kecheng_id varchar not null + constraint kecheng_users_kecheng_id_fk + references kecheng, + kecheng_status varchar not null +); + diff --git a/htyws_models/migrations/2022-10-16-161019_modify_kecheng_repeat_tbl/down.sql b/htyws_models/migrations/2022-10-16-161019_modify_kecheng_repeat_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-16-161019_modify_kecheng_repeat_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-16-161019_modify_kecheng_repeat_tbl/up.sql b/htyws_models/migrations/2022-10-16-161019_modify_kecheng_repeat_tbl/up.sql new file mode 100644 index 0000000..d9ad386 --- /dev/null +++ b/htyws_models/migrations/2022-10-16-161019_modify_kecheng_repeat_tbl/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table kecheng_repeat + drop column kecheng_type; + +alter table kecheng_repeat + add repeat_cycle_days int not null; + diff --git a/htyws_models/migrations/2022-10-16-161152_modify_kecheng_tbk/down.sql b/htyws_models/migrations/2022-10-16-161152_modify_kecheng_tbk/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-16-161152_modify_kecheng_tbk/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-16-161152_modify_kecheng_tbk/up.sql b/htyws_models/migrations/2022-10-16-161152_modify_kecheng_tbk/up.sql new file mode 100644 index 0000000..e7f37b6 --- /dev/null +++ b/htyws_models/migrations/2022-10-16-161152_modify_kecheng_tbk/up.sql @@ -0,0 +1,7 @@ +-- Your SQL goes here + +alter table kecheng + add kecheng_type varchar; + +comment on column kecheng.kecheng_type is '未来可能用前端的在线上课功能'; + diff --git a/htyws_models/migrations/2022-10-16-161428_clean_kecheng_tbls/down.sql b/htyws_models/migrations/2022-10-16-161428_clean_kecheng_tbls/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-16-161428_clean_kecheng_tbls/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-16-161428_clean_kecheng_tbls/up.sql b/htyws_models/migrations/2022-10-16-161428_clean_kecheng_tbls/up.sql new file mode 100644 index 0000000..6bac7b6 --- /dev/null +++ b/htyws_models/migrations/2022-10-16-161428_clean_kecheng_tbls/up.sql @@ -0,0 +1,14 @@ +-- Your SQL goes here + +alter table kecheng_repeat + drop column kecheng_root_id; + +alter table kecheng + add parent_id varchar not null; + +comment on column kecheng.parent_id is '第一条数据的`parent_id`是自己。'; + +alter table kecheng + add constraint kecheng_kecheng_id_fk + foreign key (parent_id) references kecheng; + diff --git a/htyws_models/migrations/2022-10-16-163323_add_user_type_to_kecheng_tbls/down.sql b/htyws_models/migrations/2022-10-16-163323_add_user_type_to_kecheng_tbls/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-16-163323_add_user_type_to_kecheng_tbls/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-16-163323_add_user_type_to_kecheng_tbls/up.sql b/htyws_models/migrations/2022-10-16-163323_add_user_type_to_kecheng_tbls/up.sql new file mode 100644 index 0000000..cb0b536 --- /dev/null +++ b/htyws_models/migrations/2022-10-16-163323_add_user_type_to_kecheng_tbls/up.sql @@ -0,0 +1,7 @@ +-- Your SQL goes here + +alter table kecheng_users + add user_type varchar not null; + +comment on column kecheng_users.user_type is '老师,学生,助教,等等'; + diff --git a/htyws_models/migrations/2022-10-17-185610_add_fields_to_kecheng_repeat/down.sql b/htyws_models/migrations/2022-10-17-185610_add_fields_to_kecheng_repeat/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-17-185610_add_fields_to_kecheng_repeat/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-17-185610_add_fields_to_kecheng_repeat/up.sql b/htyws_models/migrations/2022-10-17-185610_add_fields_to_kecheng_repeat/up.sql new file mode 100644 index 0000000..3a5927d --- /dev/null +++ b/htyws_models/migrations/2022-10-17-185610_add_fields_to_kecheng_repeat/up.sql @@ -0,0 +1,12 @@ +-- Your SQL goes here + +alter table kecheng_repeat + add repeat_end timestamp; + +comment on column kecheng_repeat.repeat_end is '重复课程的结束时间'; + +alter table kecheng_repeat + add repeat_status varchar not null; + +comment on column kecheng_repeat.repeat_status is 'ON_GOING / STOPPED / PENDING / ... / STATUS优先级大于`repeat_end`'; + diff --git a/htyws_models/migrations/2022-10-19-145429_add_created_fields_to_kecheng/down.sql b/htyws_models/migrations/2022-10-19-145429_add_created_fields_to_kecheng/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-19-145429_add_created_fields_to_kecheng/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-19-145429_add_created_fields_to_kecheng/up.sql b/htyws_models/migrations/2022-10-19-145429_add_created_fields_to_kecheng/up.sql new file mode 100644 index 0000000..356e0dc --- /dev/null +++ b/htyws_models/migrations/2022-10-19-145429_add_created_fields_to_kecheng/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table kecheng + add created_by varchar; + +alter table kecheng + add created_at timestamp; + diff --git a/htyws_models/migrations/2022-10-20-174102_add_note_type_to_resource_note/down.sql b/htyws_models/migrations/2022-10-20-174102_add_note_type_to_resource_note/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-20-174102_add_note_type_to_resource_note/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-20-174102_add_note_type_to_resource_note/up.sql b/htyws_models/migrations/2022-10-20-174102_add_note_type_to_resource_note/up.sql new file mode 100644 index 0000000..e54595e --- /dev/null +++ b/htyws_models/migrations/2022-10-20-174102_add_note_type_to_resource_note/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table resource_note + add note_type varchar; + diff --git a/htyws_models/migrations/2022-10-21-085410_add_fk_for_kecheng_repeat/down.sql b/htyws_models/migrations/2022-10-21-085410_add_fk_for_kecheng_repeat/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-21-085410_add_fk_for_kecheng_repeat/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-21-085410_add_fk_for_kecheng_repeat/up.sql b/htyws_models/migrations/2022-10-21-085410_add_fk_for_kecheng_repeat/up.sql new file mode 100644 index 0000000..c943baa --- /dev/null +++ b/htyws_models/migrations/2022-10-21-085410_add_fk_for_kecheng_repeat/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +alter table kecheng_repeat + add constraint kecheng_repeat_kecheng_id_fk + foreign key (kecheng_id) references kecheng; \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-28-175502_add_creator_role_key_to_comments/down.sql b/htyws_models/migrations/2022-10-28-175502_add_creator_role_key_to_comments/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-10-28-175502_add_creator_role_key_to_comments/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-10-28-175502_add_creator_role_key_to_comments/up.sql b/htyws_models/migrations/2022-10-28-175502_add_creator_role_key_to_comments/up.sql new file mode 100644 index 0000000..de0c80a --- /dev/null +++ b/htyws_models/migrations/2022-10-28-175502_add_creator_role_key_to_comments/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table comments + add creator_role_key varchar; + diff --git a/htyws_models/migrations/2022-11-09-142840_add_qumu_group_tbl/down.sql b/htyws_models/migrations/2022-11-09-142840_add_qumu_group_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-11-09-142840_add_qumu_group_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-11-09-142840_add_qumu_group_tbl/up.sql b/htyws_models/migrations/2022-11-09-142840_add_qumu_group_tbl/up.sql new file mode 100644 index 0000000..2e62bf8 --- /dev/null +++ b/htyws_models/migrations/2022-11-09-142840_add_qumu_group_tbl/up.sql @@ -0,0 +1,11 @@ +-- Your SQL goes here + +create table qumu_group +( + id varchar not null primary key unique, + group_name varchar not null, + qumu_ids jsonb, + created_at timestamp, + created_by timestamp +); + diff --git a/htyws_models/migrations/2022-11-09-144923_refactor_qumu_group/down.sql b/htyws_models/migrations/2022-11-09-144923_refactor_qumu_group/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-11-09-144923_refactor_qumu_group/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-11-09-144923_refactor_qumu_group/up.sql b/htyws_models/migrations/2022-11-09-144923_refactor_qumu_group/up.sql new file mode 100644 index 0000000..766e165 --- /dev/null +++ b/htyws_models/migrations/2022-11-09-144923_refactor_qumu_group/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table qumu_group + rename column qumu_ids to qumu_section_ids; + +alter table qumu_group + rename to qumu_section_group; + diff --git a/htyws_models/migrations/2022-11-24-162538_add_qumu_category_tbl/down.sql b/htyws_models/migrations/2022-11-24-162538_add_qumu_category_tbl/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-11-24-162538_add_qumu_category_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-11-24-162538_add_qumu_category_tbl/up.sql b/htyws_models/migrations/2022-11-24-162538_add_qumu_category_tbl/up.sql new file mode 100644 index 0000000..7d1d31a --- /dev/null +++ b/htyws_models/migrations/2022-11-24-162538_add_qumu_category_tbl/up.sql @@ -0,0 +1,11 @@ +-- Your SQL goes here + +create table qumu_category +( + category_key varchar not null + primary key + unique, + catogory_name varchar not null, + category_desc varchar +); + diff --git a/htyws_models/migrations/2022-11-24-162816_add_created_at_and_by/down.sql b/htyws_models/migrations/2022-11-24-162816_add_created_at_and_by/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-11-24-162816_add_created_at_and_by/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-11-24-162816_add_created_at_and_by/up.sql b/htyws_models/migrations/2022-11-24-162816_add_created_at_and_by/up.sql new file mode 100644 index 0000000..4c69304 --- /dev/null +++ b/htyws_models/migrations/2022-11-24-162816_add_created_at_and_by/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table qumu_category + add created_at timestamp not null; + +alter table qumu_category + add created_by varchar not null; + diff --git a/htyws_models/migrations/2022-11-24-163634_add_id_to_qumu_category/down.sql b/htyws_models/migrations/2022-11-24-163634_add_id_to_qumu_category/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-11-24-163634_add_id_to_qumu_category/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-11-24-163634_add_id_to_qumu_category/up.sql b/htyws_models/migrations/2022-11-24-163634_add_id_to_qumu_category/up.sql new file mode 100644 index 0000000..3b2c7ac --- /dev/null +++ b/htyws_models/migrations/2022-11-24-163634_add_id_to_qumu_category/up.sql @@ -0,0 +1,17 @@ +-- Your SQL goes here + +alter table qumu_category + add id varchar not null; + +alter table qumu_category + drop constraint qumu_category_pkey; + +alter table qumu_category + add primary key (id); + +alter table qumu_category + add unique (category_key); + +alter table qumu_category + add unique (id); + diff --git a/htyws_models/migrations/2022-11-25-105145_add_is_delete_for_qumu_category/down.sql b/htyws_models/migrations/2022-11-25-105145_add_is_delete_for_qumu_category/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-11-25-105145_add_is_delete_for_qumu_category/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-11-25-105145_add_is_delete_for_qumu_category/up.sql b/htyws_models/migrations/2022-11-25-105145_add_is_delete_for_qumu_category/up.sql new file mode 100644 index 0000000..4a0b18c --- /dev/null +++ b/htyws_models/migrations/2022-11-25-105145_add_is_delete_for_qumu_category/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +alter table qumu_category + add is_delete bool; \ No newline at end of file diff --git a/htyws_models/migrations/2022-11-25-105457_set_is_delete_not_null_for_qumu_category/down.sql b/htyws_models/migrations/2022-11-25-105457_set_is_delete_not_null_for_qumu_category/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-11-25-105457_set_is_delete_not_null_for_qumu_category/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-11-25-105457_set_is_delete_not_null_for_qumu_category/up.sql b/htyws_models/migrations/2022-11-25-105457_set_is_delete_not_null_for_qumu_category/up.sql new file mode 100644 index 0000000..72ab1fa --- /dev/null +++ b/htyws_models/migrations/2022-11-25-105457_set_is_delete_not_null_for_qumu_category/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +alter table qumu_category alter column is_delete set not null; + diff --git a/htyws_models/migrations/2022-11-25-110119_correct_typo_for_qumu_category/down.sql b/htyws_models/migrations/2022-11-25-110119_correct_typo_for_qumu_category/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-11-25-110119_correct_typo_for_qumu_category/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-11-25-110119_correct_typo_for_qumu_category/up.sql b/htyws_models/migrations/2022-11-25-110119_correct_typo_for_qumu_category/up.sql new file mode 100644 index 0000000..7df0963 --- /dev/null +++ b/htyws_models/migrations/2022-11-25-110119_correct_typo_for_qumu_category/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +alter table qumu_category rename column catogory_name to category_name; + diff --git a/htyws_models/migrations/2022-11-26-083330_add_qumu_category_info_into_qumu_section/down.sql b/htyws_models/migrations/2022-11-26-083330_add_qumu_category_info_into_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-11-26-083330_add_qumu_category_info_into_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-11-26-083330_add_qumu_category_info_into_qumu_section/up.sql b/htyws_models/migrations/2022-11-26-083330_add_qumu_category_info_into_qumu_section/up.sql new file mode 100644 index 0000000..0e0e715 --- /dev/null +++ b/htyws_models/migrations/2022-11-26-083330_add_qumu_category_info_into_qumu_section/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table qumu_section + add qumu_category_key varchar; + +alter table qumu_section + add qumu_category_name varchar; + diff --git a/htyws_models/migrations/2022-11-28-135155_add_is_default_to_qumu_category/down.sql b/htyws_models/migrations/2022-11-28-135155_add_is_default_to_qumu_category/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2022-11-28-135155_add_is_default_to_qumu_category/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2022-11-28-135155_add_is_default_to_qumu_category/up.sql b/htyws_models/migrations/2022-11-28-135155_add_is_default_to_qumu_category/up.sql new file mode 100644 index 0000000..ed630c7 --- /dev/null +++ b/htyws_models/migrations/2022-11-28-135155_add_is_default_to_qumu_category/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table qumu_category + add is_default boolean; + diff --git a/htyws_models/migrations/2023-05-04-131740_add_created_at_and_updated_at_in_ref_resource/down.sql b/htyws_models/migrations/2023-05-04-131740_add_created_at_and_updated_at_in_ref_resource/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2023-05-04-131740_add_created_at_and_updated_at_in_ref_resource/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2023-05-04-131740_add_created_at_and_updated_at_in_ref_resource/up.sql b/htyws_models/migrations/2023-05-04-131740_add_created_at_and_updated_at_in_ref_resource/up.sql new file mode 100644 index 0000000..16ec0ec --- /dev/null +++ b/htyws_models/migrations/2023-05-04-131740_add_created_at_and_updated_at_in_ref_resource/up.sql @@ -0,0 +1,7 @@ +-- Your SQL goes here +alter table ref_resources + add created_at timestamp; + +alter table ref_resources + add updated_at timestamp; + diff --git a/htyws_models/migrations/2023-05-21-064712_add_is_draft_for_qumu_section/down.sql b/htyws_models/migrations/2023-05-21-064712_add_is_draft_for_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2023-05-21-064712_add_is_draft_for_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2023-05-21-064712_add_is_draft_for_qumu_section/up.sql b/htyws_models/migrations/2023-05-21-064712_add_is_draft_for_qumu_section/up.sql new file mode 100644 index 0000000..1ba3cc7 --- /dev/null +++ b/htyws_models/migrations/2023-05-21-064712_add_is_draft_for_qumu_section/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +alter table qumu_section + add is_draft bool; + diff --git a/htyws_models/migrations/2023-05-21-170715_add_compress_processed/down.sql b/htyws_models/migrations/2023-05-21-170715_add_compress_processed/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2023-05-21-170715_add_compress_processed/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2023-05-21-170715_add_compress_processed/up.sql b/htyws_models/migrations/2023-05-21-170715_add_compress_processed/up.sql new file mode 100644 index 0000000..4dc9f11 --- /dev/null +++ b/htyws_models/migrations/2023-05-21-170715_add_compress_processed/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +alter table ref_resources + add compress_processed bool; + diff --git a/htyws_models/migrations/2023-05-29-063511_qumu_id_can_be_null/down.sql b/htyws_models/migrations/2023-05-29-063511_qumu_id_can_be_null/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2023-05-29-063511_qumu_id_can_be_null/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2023-05-29-063511_qumu_id_can_be_null/up.sql b/htyws_models/migrations/2023-05-29-063511_qumu_id_can_be_null/up.sql new file mode 100644 index 0000000..ff24e7e --- /dev/null +++ b/htyws_models/migrations/2023-05-29-063511_qumu_id_can_be_null/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + + +alter table qumu_section + alter column qumu_id drop not null; + diff --git a/htyws_models/migrations/2023-05-29-065204_remove_qumu_id_constraint/down.sql b/htyws_models/migrations/2023-05-29-065204_remove_qumu_id_constraint/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2023-05-29-065204_remove_qumu_id_constraint/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2023-05-29-065204_remove_qumu_id_constraint/up.sql b/htyws_models/migrations/2023-05-29-065204_remove_qumu_id_constraint/up.sql new file mode 100644 index 0000000..b827f04 --- /dev/null +++ b/htyws_models/migrations/2023-05-29-065204_remove_qumu_id_constraint/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table qumu_section + drop constraint qumu_section_qumu_id_fk; + diff --git a/htyws_models/migrations/2023-06-02-054812_add_cloned_from_to_qumu_section/down.sql b/htyws_models/migrations/2023-06-02-054812_add_cloned_from_to_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2023-06-02-054812_add_cloned_from_to_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2023-06-02-054812_add_cloned_from_to_qumu_section/up.sql b/htyws_models/migrations/2023-06-02-054812_add_cloned_from_to_qumu_section/up.sql new file mode 100644 index 0000000..05fc6ea --- /dev/null +++ b/htyws_models/migrations/2023-06-02-054812_add_cloned_from_to_qumu_section/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + + +alter table qumu_section + add cloned_from varchar; + diff --git a/htyws_models/migrations/2023-06-03-142740_add_created_at_and_created_by_ref_resources/down.sql b/htyws_models/migrations/2023-06-03-142740_add_created_at_and_created_by_ref_resources/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2023-06-03-142740_add_created_at_and_created_by_ref_resources/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2023-06-03-142740_add_created_at_and_created_by_ref_resources/up.sql b/htyws_models/migrations/2023-06-03-142740_add_created_at_and_created_by_ref_resources/up.sql new file mode 100644 index 0000000..2e70cc7 --- /dev/null +++ b/htyws_models/migrations/2023-06-03-142740_add_created_at_and_created_by_ref_resources/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table ref_resources + add created_by varchar; + diff --git a/htyws_models/migrations/2023-06-15-171733_add_synced_column_to_ref_resources/down.sql b/htyws_models/migrations/2023-06-15-171733_add_synced_column_to_ref_resources/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2023-06-15-171733_add_synced_column_to_ref_resources/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2023-06-15-171733_add_synced_column_to_ref_resources/up.sql b/htyws_models/migrations/2023-06-15-171733_add_synced_column_to_ref_resources/up.sql new file mode 100644 index 0000000..1866a46 --- /dev/null +++ b/htyws_models/migrations/2023-06-15-171733_add_synced_column_to_ref_resources/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +alter table ref_resources + add synced_with_hty_resource boolean; + diff --git a/htyws_models/migrations/2023-06-16-174203_add_created_by_and_updated_by_to_ref_resources/down.sql b/htyws_models/migrations/2023-06-16-174203_add_created_by_and_updated_by_to_ref_resources/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2023-06-16-174203_add_created_by_and_updated_by_to_ref_resources/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2023-06-16-174203_add_created_by_and_updated_by_to_ref_resources/up.sql b/htyws_models/migrations/2023-06-16-174203_add_created_by_and_updated_by_to_ref_resources/up.sql new file mode 100644 index 0000000..2fac72d --- /dev/null +++ b/htyws_models/migrations/2023-06-16-174203_add_created_by_and_updated_by_to_ref_resources/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table ref_resources + add updated_by varchar; + diff --git a/htyws_models/migrations/2023-07-12-165202_add_versioned_data_table/down.sql b/htyws_models/migrations/2023-07-12-165202_add_versioned_data_table/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2023-07-12-165202_add_versioned_data_table/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2023-07-12-165202_add_versioned_data_table/up.sql b/htyws_models/migrations/2023-07-12-165202_add_versioned_data_table/up.sql new file mode 100644 index 0000000..b66e3a6 --- /dev/null +++ b/htyws_models/migrations/2023-07-12-165202_add_versioned_data_table/up.sql @@ -0,0 +1,13 @@ +-- Your SQL goes here +create table versioned_data +( + id varchar not null primary key unique, + ref_id varchar not null, + ref_type varchar not null, + created_at timestamp not null, + create_by varchar not null, + data_type varchar not null, + from_data jsonb, + to_data jsonb +); + diff --git a/htyws_models/migrations/2023-08-07-154709_add_updated_by_and_updated_at_for_qumu_section/down.sql b/htyws_models/migrations/2023-08-07-154709_add_updated_by_and_updated_at_for_qumu_section/down.sql new file mode 100644 index 0000000..291a97c --- /dev/null +++ b/htyws_models/migrations/2023-08-07-154709_add_updated_by_and_updated_at_for_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/htyws_models/migrations/2023-08-07-154709_add_updated_by_and_updated_at_for_qumu_section/up.sql b/htyws_models/migrations/2023-08-07-154709_add_updated_by_and_updated_at_for_qumu_section/up.sql new file mode 100644 index 0000000..24038f2 --- /dev/null +++ b/htyws_models/migrations/2023-08-07-154709_add_updated_by_and_updated_at_for_qumu_section/up.sql @@ -0,0 +1,7 @@ +-- Your SQL goes here +alter table qumu_section + add updated_at timestamp; + +alter table qumu_section + add updated_by varchar; + diff --git a/htyws_models/migrations/2023-09-30-155237_add_teachers_to_daka/down.sql b/htyws_models/migrations/2023-09-30-155237_add_teachers_to_daka/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2023-09-30-155237_add_teachers_to_daka/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2023-09-30-155237_add_teachers_to_daka/up.sql b/htyws_models/migrations/2023-09-30-155237_add_teachers_to_daka/up.sql new file mode 100644 index 0000000..64f623b --- /dev/null +++ b/htyws_models/migrations/2023-09-30-155237_add_teachers_to_daka/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here + +alter table daka + add teachers jsonb; + +-- 具体格式: { teacher_id: xxx, teacher_name: yyy } + + diff --git a/htyws_models/migrations/2023-10-08-145941_add_teachers_to_qumu_section/down.sql b/htyws_models/migrations/2023-10-08-145941_add_teachers_to_qumu_section/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2023-10-08-145941_add_teachers_to_qumu_section/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2023-10-08-145941_add_teachers_to_qumu_section/up.sql b/htyws_models/migrations/2023-10-08-145941_add_teachers_to_qumu_section/up.sql new file mode 100644 index 0000000..6174894 --- /dev/null +++ b/htyws_models/migrations/2023-10-08-145941_add_teachers_to_qumu_section/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here + +alter table qumu_section + add teachers jsonb; diff --git a/htyws_models/migrations/2023-10-08-163633_add_acl_tbl/down.sql b/htyws_models/migrations/2023-10-08-163633_add_acl_tbl/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2023-10-08-163633_add_acl_tbl/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2023-10-08-163633_add_acl_tbl/up.sql b/htyws_models/migrations/2023-10-08-163633_add_acl_tbl/up.sql new file mode 100644 index 0000000..d94946b --- /dev/null +++ b/htyws_models/migrations/2023-10-08-163633_add_acl_tbl/up.sql @@ -0,0 +1,18 @@ +-- Your SQL goes here + +create table acl +( + id varchar not null + constraint acl_pk + primary key, + from_id varchar not null, + from_type varchar not null, + to_id varchar not null, + to_type varchar not null, + actions jsonb, + created_at timestamp, + updated_at timestamp, + created_by varchar, + updated_by varchar +); + diff --git a/htyws_models/migrations/2023-10-14-175649_add_updated_at_updated_by_to_daka/down.sql b/htyws_models/migrations/2023-10-14-175649_add_updated_at_updated_by_to_daka/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2023-10-14-175649_add_updated_at_updated_by_to_daka/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2023-10-14-175649_add_updated_at_updated_by_to_daka/up.sql b/htyws_models/migrations/2023-10-14-175649_add_updated_at_updated_by_to_daka/up.sql new file mode 100644 index 0000000..de0a8e1 --- /dev/null +++ b/htyws_models/migrations/2023-10-14-175649_add_updated_at_updated_by_to_daka/up.sql @@ -0,0 +1,6 @@ +alter table daka + add updated_at timestamp; + +alter table daka + add updated_by varchar; + diff --git a/htyws_models/migrations/2023-11-12-172533_add_teachers_to_qumu_section_group/down.sql b/htyws_models/migrations/2023-11-12-172533_add_teachers_to_qumu_section_group/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2023-11-12-172533_add_teachers_to_qumu_section_group/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2023-11-12-172533_add_teachers_to_qumu_section_group/up.sql b/htyws_models/migrations/2023-11-12-172533_add_teachers_to_qumu_section_group/up.sql new file mode 100644 index 0000000..90177e3 --- /dev/null +++ b/htyws_models/migrations/2023-11-12-172533_add_teachers_to_qumu_section_group/up.sql @@ -0,0 +1,15 @@ +-- Your SQL goes here + +alter table qumu_section_group +alter column created_by type varchar using created_by::varchar; + +alter table qumu_section_group + add updated_at timestamp; + +alter table qumu_section_group + add updated_by varchar; + +alter table qumu_section_group + add teachers jsonb; + + diff --git a/htyws_models/migrations/2023-11-24-144817_add_meta_to_resource_note/down.sql b/htyws_models/migrations/2023-11-24-144817_add_meta_to_resource_note/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2023-11-24-144817_add_meta_to_resource_note/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2023-11-24-144817_add_meta_to_resource_note/up.sql b/htyws_models/migrations/2023-11-24-144817_add_meta_to_resource_note/up.sql new file mode 100644 index 0000000..e6d461e --- /dev/null +++ b/htyws_models/migrations/2023-11-24-144817_add_meta_to_resource_note/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table resource_note + add meta jsonb; + diff --git a/htyws_models/migrations/2023-11-24-161407_add_students_to_daka/down.sql b/htyws_models/migrations/2023-11-24-161407_add_students_to_daka/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2023-11-24-161407_add_students_to_daka/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2023-11-24-161407_add_students_to_daka/up.sql b/htyws_models/migrations/2023-11-24-161407_add_students_to_daka/up.sql new file mode 100644 index 0000000..817d728 --- /dev/null +++ b/htyws_models/migrations/2023-11-24-161407_add_students_to_daka/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + +alter table daka + add students jsonb; + + diff --git a/htyws_models/migrations/2023-12-03-154959_add_qumu_section_data_to_jihua/down.sql b/htyws_models/migrations/2023-12-03-154959_add_qumu_section_data_to_jihua/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2023-12-03-154959_add_qumu_section_data_to_jihua/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2023-12-03-154959_add_qumu_section_data_to_jihua/up.sql b/htyws_models/migrations/2023-12-03-154959_add_qumu_section_data_to_jihua/up.sql new file mode 100644 index 0000000..f1d35b6 --- /dev/null +++ b/htyws_models/migrations/2023-12-03-154959_add_qumu_section_data_to_jihua/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +alter table jihua + add qumu_section_data jsonb; + diff --git a/htyws_models/migrations/2023-12-03-164416_remove_qumu_section_data_col/down.sql b/htyws_models/migrations/2023-12-03-164416_remove_qumu_section_data_col/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2023-12-03-164416_remove_qumu_section_data_col/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2023-12-03-164416_remove_qumu_section_data_col/up.sql b/htyws_models/migrations/2023-12-03-164416_remove_qumu_section_data_col/up.sql new file mode 100644 index 0000000..4f4acb5 --- /dev/null +++ b/htyws_models/migrations/2023-12-03-164416_remove_qumu_section_data_col/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + +alter table jihua +drop column qumu_section_data; + + diff --git a/htyws_models/migrations/2023-12-09-162707_add_qumu_section_datas_col_to_jihua/down.sql b/htyws_models/migrations/2023-12-09-162707_add_qumu_section_datas_col_to_jihua/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2023-12-09-162707_add_qumu_section_datas_col_to_jihua/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2023-12-09-162707_add_qumu_section_datas_col_to_jihua/up.sql b/htyws_models/migrations/2023-12-09-162707_add_qumu_section_datas_col_to_jihua/up.sql new file mode 100644 index 0000000..dc308af --- /dev/null +++ b/htyws_models/migrations/2023-12-09-162707_add_qumu_section_datas_col_to_jihua/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table jihua + add qumu_sections jsonb; + diff --git a/htyws_models/migrations/2023-12-27-160739_add_qumu_sections_to_daka/down.sql b/htyws_models/migrations/2023-12-27-160739_add_qumu_sections_to_daka/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2023-12-27-160739_add_qumu_sections_to_daka/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2023-12-27-160739_add_qumu_sections_to_daka/up.sql b/htyws_models/migrations/2023-12-27-160739_add_qumu_sections_to_daka/up.sql new file mode 100644 index 0000000..5a3b357 --- /dev/null +++ b/htyws_models/migrations/2023-12-27-160739_add_qumu_sections_to_daka/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here + +alter table daka + add qumu_sections jsonb; diff --git a/htyws_models/migrations/2024-01-19-091325_drop_kecheng_tbls/down.sql b/htyws_models/migrations/2024-01-19-091325_drop_kecheng_tbls/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2024-01-19-091325_drop_kecheng_tbls/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2024-01-19-091325_drop_kecheng_tbls/up.sql b/htyws_models/migrations/2024-01-19-091325_drop_kecheng_tbls/up.sql new file mode 100644 index 0000000..8653ccb --- /dev/null +++ b/htyws_models/migrations/2024-01-19-091325_drop_kecheng_tbls/up.sql @@ -0,0 +1,9 @@ +-- Your SQL goes here + +drop table kecheng_repeat; + +drop table kecheng_users; + +drop table kecheng; + + diff --git a/htyws_models/migrations/2024-01-23-094827_add_created_at_and_created_by/down.sql b/htyws_models/migrations/2024-01-23-094827_add_created_at_and_created_by/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2024-01-23-094827_add_created_at_and_created_by/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2024-01-23-094827_add_created_at_and_created_by/up.sql b/htyws_models/migrations/2024-01-23-094827_add_created_at_and_created_by/up.sql new file mode 100644 index 0000000..5171d32 --- /dev/null +++ b/htyws_models/migrations/2024-01-23-094827_add_created_at_and_created_by/up.sql @@ -0,0 +1,10 @@ +-- Your SQL goes here + +alter table jihua + add created_by varchar; + +alter table jihua + add created_at timestamp; + +-- column reordering is not supported jihua.created_by + diff --git a/htyws_models/migrations/2024-03-24-171646_loose_not_null_of_group_id_of_daka/down.sql b/htyws_models/migrations/2024-03-24-171646_loose_not_null_of_group_id_of_daka/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2024-03-24-171646_loose_not_null_of_group_id_of_daka/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2024-03-24-171646_loose_not_null_of_group_id_of_daka/up.sql b/htyws_models/migrations/2024-03-24-171646_loose_not_null_of_group_id_of_daka/up.sql new file mode 100644 index 0000000..de154a9 --- /dev/null +++ b/htyws_models/migrations/2024-03-24-171646_loose_not_null_of_group_id_of_daka/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here + +alter table daka + alter column group_id drop not null; + diff --git a/htyws_models/migrations/2024-04-13-161134_add_is_yanqi_to_daka/down.sql b/htyws_models/migrations/2024-04-13-161134_add_is_yanqi_to_daka/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/htyws_models/migrations/2024-04-13-161134_add_is_yanqi_to_daka/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/htyws_models/migrations/2024-04-13-161134_add_is_yanqi_to_daka/up.sql b/htyws_models/migrations/2024-04-13-161134_add_is_yanqi_to_daka/up.sql new file mode 100644 index 0000000..bcf30f3 --- /dev/null +++ b/htyws_models/migrations/2024-04-13-161134_add_is_yanqi_to_daka/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here + +alter table daka + add is_yanqi boolean; diff --git a/htyws_models/src/lib.rs b/htyws_models/src/lib.rs new file mode 100644 index 0000000..04f61da --- /dev/null +++ b/htyws_models/src/lib.rs @@ -0,0 +1,11 @@ +#[macro_use] +extern crate diesel; +#[macro_use] +extern crate serde_derive; +// extern crate time; + +pub mod models; +mod schema; + +// Re-export pagination functionality from htycommons +pub use htycommons::pagination::*; diff --git a/local_build.sh b/local_build.sh new file mode 100755 index 0000000..0722070 --- /dev/null +++ b/local_build.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -x +docker pull ghcr.io/alchemy-studio/htybasic + +OPERATOR=1 +OPERATOR_PWD=1 +FORM_API_KEY=1 + +function build_htyws() { + echo 'build htyws...' + docker build -f htyws/container/local/Dockerfile \ + -t ghcr.io/alchemy-studio/htyws:latest \ + --build-arg OPERATOR=${OPERATOR} \ + --build-arg OPERATOR_PWD=${OPERATOR_PWD} \ + --build-arg FORM_API_KEY=${FORM_API_KEY} \ + . +} + + +function build_htyuc() { + echo 'build htyuc...' + docker build -f htyuc/container/local/Dockerfile \ + -t ghcr.io/alchemy-studio/htyuc:latest \ + --build-arg OPERATOR=${OPERATOR} \ + --build-arg OPERATOR_PWD=${OPERATOR_PWD} \ + --build-arg FORM_API_KEY=${FORM_API_KEY} . +} + +case "$1" in + htyws) build_htyws ;; + htyuc) build_htyuc ;; + *) build_htyws; build_htyuc; +esac + +echo "done" diff --git a/rand.sh b/rand.sh new file mode 100755 index 0000000..72bc16b --- /dev/null +++ b/rand.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +#LC_CTYPE=C tr -dc A-Za-z0-9 < /dev/random | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1 | tr '[:upper:]' '[:lower:]' +LC_CTYPE=C tr -dc 'a-z' < /dev/random | fold -w 32 | head -n 1 diff --git a/run_rotate.sh b/run_rotate.sh new file mode 100755 index 0000000..494c93f --- /dev/null +++ b/run_rotate.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -x +HUIWING="/mnt/huiwing/huiwing" +cd $HUIWING/htyuc || exit +/usr/sbin/logrotate -v -s ./logrotate.status ./logrotate.config + +cd $HUIWING/htyws || exit +/usr/sbin/logrotate -v -s ./logrotate.status ./logrotate.config + +AUTHCORE="/mnt/huiwing/AuthCore" +cd $AUTHCORE/htyuc || exit +/usr/sbin/logrotate -v -s ./logrotate.status ./logrotate.config diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..cd31a1f --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,2 @@ +# https://github.com/rust-lang/rustfmt#rusts-editions +edition = "2018" \ No newline at end of file diff --git a/scripts/ci-authcore-weekly.sh b/scripts/ci-authcore-weekly.sh new file mode 100755 index 0000000..4844d04 --- /dev/null +++ b/scripts/ci-authcore-weekly.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# 在已启动 Postgres(htyts 库)、Redis、以及 AuthCore `htyuc`(与 JWT_KEY 一致)的前提下,运行联调测试。 +# `HTYUC_URL` 指向 `htyuc` 根地址(如 http://127.0.0.1:18080)。`TS_DATABASE_URL` 必填。 +# 本地从零起容器 + 迁移 + htyuc + 本测试:./scripts/run-authcore-e2e-docker.sh(`docker-compose.authcore-e2e.yml`)。 +set -euo pipefail +cd "$(dirname "$0")/.." + +export HTYUC_URL="${HTYUC_URL:-http://127.0.0.1:18080}" +export JWT_KEY="${JWT_KEY:-test_jwt_key_for_testing_only_1234567890}" +export TOKEN_VERIFY="${TOKEN_VERIFY:-true}" +export AUTH_CHECKING="${AUTH_CHECKING:-true}" +export REDIS_HOST="${REDIS_HOST:-127.0.0.1}" +export REDIS_PORT="${REDIS_PORT:-6379}" +export TS_DOMAIN="${TS_DOMAIN:-e2e.test.local}" +export POOL_SIZE="${POOL_SIZE:-8}" + +: "${TS_DATABASE_URL:?set TS_DATABASE_URL to htyts Postgres URL}" + +exec cargo test -p htyts --test ts_e2e_authcore_http -- --ignored --test-threads=1 --nocapture diff --git a/scripts/run-authcore-e2e-docker.sh b/scripts/run-authcore-e2e-docker.sh new file mode 100755 index 0000000..d95a4cb --- /dev/null +++ b/scripts/run-authcore-e2e-docker.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# 启动 docker-compose.authcore-e2e.yml → 迁移与 UC 种子数据 → 构建并启动 htyuc → 运行 ts_e2e_authcore_http(--ignored)。 +# 依赖:docker、psql、diesel_cli、cargo;AuthCore 默认路径为仓库上一级的 AuthCore。 +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +AUTHCORE="${AUTHCORE_ROOT:-$ROOT/../AuthCore}" +COMPOSE_FILE="$ROOT/docker-compose.authcore-e2e.yml" + +export TS_DATABASE_URL="${TS_DATABASE_URL:-postgres://htyts_e2e:htyts_e2e@127.0.0.1:15432/htyts_e2e}" +export UC_DB_URL="${UC_DB_URL:-postgres://htyuc:htyuc@127.0.0.1:15433/htyuc_test}" +export REDIS_HOST="${REDIS_HOST:-127.0.0.1}" +export REDIS_PORT="${REDIS_PORT:-16379}" +export HTYUC_URL="${HTYUC_URL:-http://127.0.0.1:18080}" +export UC_PORT="${UC_PORT:-18080}" +export JWT_KEY="${JWT_KEY:-test_jwt_key_for_testing_only_1234567890}" +export TOKEN_VERIFY="${TOKEN_VERIFY:-true}" +export AUTH_CHECKING="${AUTH_CHECKING:-true}" +export TS_DOMAIN="${TS_DOMAIN:-e2e.test.local}" +export POOL_SIZE="${POOL_SIZE:-8}" +export ZOMBIE_MIN="${ZOMBIE_MIN:-10}" +export EXPIRATION_DAYS="${EXPIRATION_DAYS:-7}" +export SKIP_POST_LOGIN="${SKIP_POST_LOGIN:-true}" +export SKIP_REGISTRATION="${SKIP_REGISTRATION:-true}" +export LOGGER_LEVEL="${LOGGER_LEVEL:-INFO}" + +if [[ ! -f "$AUTHCORE/Cargo.toml" ]]; then + echo "AuthCore not found at $AUTHCORE; set AUTHCORE_ROOT" >&2 + exit 1 +fi + +cd "$ROOT" +docker compose -f "$COMPOSE_FILE" up -d --wait + +command -v diesel >/dev/null 2>&1 || { + echo "diesel_cli not found; install: cargo install diesel_cli --no-default-features --features postgres --locked" >&2 + exit 1 +} + +echo "==> htyts_models diesel migration" +(cd "$ROOT/htyts_models" && DATABASE_URL="$TS_DATABASE_URL" diesel migration run) + +echo "==> htyuc_models diesel" +(cd "$AUTHCORE/htyuc_models" && DATABASE_URL="$UC_DB_URL" diesel setup && diesel migration run) + +echo "==> UC init_test_data.sql" +PGPASSWORD=htyuc psql -h 127.0.0.1 -p 15433 -U htyuc -d htyuc_test -f "$AUTHCORE/htyuc/tests/fixtures/init_test_data.sql" + +echo "==> cargo build htyuc (release)" +# 避免 IDE/CI 注入的 CARGO_TARGET_DIR 使可执行文件不在 $AUTHCORE/target/release +(cd "$AUTHCORE" && env -u CARGO_TARGET_DIR cargo build --release -p htyuc) + +HTYUC_BIN="$AUTHCORE/target/release/htyuc" +if [[ ! -x "$HTYUC_BIN" ]]; then + echo "missing $HTYUC_BIN (after env -u CARGO_TARGET_DIR build)" >&2 + exit 1 +fi + +cleanup() { + if [[ -n "${HTYUC_PID:-}" ]] && kill -0 "$HTYUC_PID" 2>/dev/null; then + kill "$HTYUC_PID" 2>/dev/null || true + fi +} +trap cleanup EXIT + +echo "==> start htyuc (port $UC_PORT)" +nohup env UC_DB_URL="$UC_DB_URL" REDIS_HOST="$REDIS_HOST" REDIS_PORT="$REDIS_PORT" \ + JWT_KEY="$JWT_KEY" UC_PORT="$UC_PORT" POOL_SIZE=5 EXPIRATION_DAYS="$EXPIRATION_DAYS" \ + SKIP_POST_LOGIN="$SKIP_POST_LOGIN" SKIP_REGISTRATION="$SKIP_REGISTRATION" \ + LOGGER_LEVEL="$LOGGER_LEVEL" \ + "$HTYUC_BIN" > /tmp/htyuc-authcore-e2e.log 2>&1 & +HTYUC_PID=$! + +for _ in $(seq 1 90); do + if curl -fsS "http://127.0.0.1:${UC_PORT}/api/v1/uc/index" >/dev/null 2>&1; then + echo "==> htyuc ready" + break + fi + sleep 1 +done +if ! curl -fsS "http://127.0.0.1:${UC_PORT}/api/v1/uc/index" >/dev/null 2>&1; then + echo "htyuc did not become ready; log:" >&2 + tail -80 /tmp/htyuc-authcore-e2e.log >&2 || true + exit 1 +fi + +echo "==> cargo test ts_e2e_authcore_http" +cd "$ROOT" +cargo test -p htyts --test ts_e2e_authcore_http -- --ignored --test-threads=1 --nocapture diff --git a/scripts/run-ts-e2e.sh b/scripts/run-ts-e2e.sh new file mode 100755 index 0000000..8d434f9 --- /dev/null +++ b/scripts/run-ts-e2e.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Start docker-compose for htyts e2e, run cargo integration tests, then stop containers. +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" + +COMPOSE=(docker compose -f docker-compose.ts-e2e.yml) + +"${COMPOSE[@]}" up -d --wait + +cleanup() { + "${COMPOSE[@]}" down +} +trap cleanup EXIT + +export TS_DATABASE_URL="${TS_DATABASE_URL:-postgres://htyts_e2e:htyts_e2e@127.0.0.1:5436/htyts_e2e}" + +if ! command -v diesel >/dev/null 2>&1; then + echo "Installing diesel_cli (one-time) for migrations..." + cargo install diesel_cli --no-default-features --features postgres --locked +fi +( + cd "$ROOT/htyts_models" + diesel migration run --database-url "$TS_DATABASE_URL" +) + +# Clean slate so repeated runs are deterministic (sudoer JWT is re-seeded in tests). +docker compose -f docker-compose.ts-e2e.yml exec -T ts-e2e-db \ + psql -U htyts_e2e -d htyts_e2e -c "TRUNCATE TABLE dbtask;" || true +docker compose -f docker-compose.ts-e2e.yml exec -T ts-e2e-redis redis-cli FLUSHDB || true + +export JWT_KEY="${JWT_KEY:-e2e-jwt-secret-key-minimum-32-chars!!}" +export REDIS_HOST="${REDIS_HOST:-127.0.0.1}" +export REDIS_PORT="${REDIS_PORT:-6390}" +export TS_DOMAIN="${TS_DOMAIN:-e2e.test.local}" +export ZOMBIE_MIN="${ZOMBIE_MIN:-10}" +export POOL_SIZE="${POOL_SIZE:-8}" + +cargo test -p htyts --test ts_e2e_http -- --nocapture