diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2e973b55b5..3f0cfa40c7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -59,7 +59,7 @@ test:build: rules: - if: $TEST == 'false' when: never - - if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event' + - if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop' changes: paths: - packages/backend-rs/**/* @@ -84,7 +84,7 @@ test:build:backend_ts_only: rules: - if: $TEST == 'false' when: never - - if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event' + - if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop' changes: paths: - packages/backend-rs/**/* @@ -94,7 +94,7 @@ test:build:backend_ts_only: - Cargo.toml - Cargo.lock when: never - - if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event' + - if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop' changes: paths: - packages/backend/**/* @@ -124,7 +124,7 @@ test:build:client_only: rules: - if: $TEST == 'false' when: never - - if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event' + - if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop' changes: paths: - packages/backend-rs/**/* @@ -134,7 +134,7 @@ test:build:client_only: - Cargo.toml - Cargo.lock when: never - - if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event' + - if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop' changes: paths: - packages/client/**/* @@ -157,7 +157,7 @@ test:build:client_only: build:container: stage: build - image: docker.io/debian:bookworm-slim + image: quay.io/buildah/stable:latest services: [] rules: - if: $BUILD == 'false' @@ -181,20 +181,17 @@ build:container: optional: true - job: test:build:client_only optional: true + variables: + STORAGE_DRIVER: overlay2 before_script: - - apt-get update && apt-get -y upgrade - - apt-get install -y --no-install-recommends buildah ca-certificates fuse-overlayfs - - buildah login --username "${CI_REGISTRY_USER}" --password "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}" + - buildah version + - buildah prune --all --force || true + - echo "${CI_REGISTRY_PASSWORD}" | buildah login --username "${CI_REGISTRY_USER}" --password-stdin "${CI_REGISTRY}" - export IMAGE_TAG="${CI_REGISTRY}/${CI_PROJECT_PATH}/develop:not-for-production" - export IMAGE_CACHE="${CI_REGISTRY}/${CI_PROJECT_PATH}/develop/cache" script: - |- buildah build \ - --isolation chroot \ - --device /dev/fuse:rw \ - --security-opt seccomp=unconfined \ - --security-opt apparmor=unconfined \ - --cap-add all \ --platform linux/amd64 \ --layers \ --cache-to "${IMAGE_CACHE}" \ @@ -209,23 +206,17 @@ cargo:test: rules: - if: $TEST == 'false' when: never - - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'main' - when: never - - if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event' + - if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop' changes: paths: - packages/backend-rs/**/* - packages/macro-rs/**/* - Cargo.toml - Cargo.lock + - package.json when: always script: - curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C /usr/local/cargo/bin - - pnpm install --frozen-lockfile - - mkdir -p packages/backend-rs/built - - cp packages/backend-rs/index.js packages/backend-rs/built/index.js - - cp packages/backend-rs/index.d.ts packages/backend-rs/built/index.d.ts - - pnpm --filter='!backend-rs' run build:debug - cargo test --doc - cargo nextest run @@ -234,9 +225,7 @@ cargo:clippy: rules: - if: $TEST == 'false' when: never - - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'main' - when: never - - if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event' + - if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop' changes: paths: - packages/backend-rs/**/* diff --git a/COPYING b/COPYING index 96d91510d3..3a02cb3f81 100644 --- a/COPYING +++ b/COPYING @@ -30,6 +30,10 @@ Chiptune2.js by Simon Gündling License: MIT https://github.com/deskjet/chiptune2.js#license +bb8-redis by Kyle Huey +License: MIT +https://github.com/djc/bb8/blob/62597aa45ac1746780b08cb6a68cf7d65452a23a/LICENSE + Licenses for all softwares and software libraries installed via the Node Package Manager ("npm") can be found by running the following shell command in the root directory of this repository: pnpm licenses list diff --git a/Cargo.lock b/Cargo.lock index 50ab4c1b4a..e8fdd48288 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,9 +85,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "18b8795de6d09abb2b178fa5a9e3bb10da935750f33449a132b328b9391b2c6a" [[package]] name = "arbitrary" @@ -103,7 +103,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -154,7 +154,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -165,7 +165,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -211,7 +211,9 @@ name = "backend-rs" version = "0.0.0" dependencies = [ "argon2", + "async-trait", "basen", + "bb8", "bcrypt", "chrono", "cuid2", @@ -219,7 +221,7 @@ dependencies = [ "idna", "image", "isahc", - "macro_rs", + "macro-rs", "napi", "napi-build", "napi-derive", @@ -239,6 +241,7 @@ dependencies = [ "sysinfo", "thiserror", "tokio", + "tokio-test", "tracing", "tracing-subscriber", "url", @@ -297,6 +300,19 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dbe4bb73fd931c4d1aaf53b35d1286c8a948ad00ec92c8e3c856f15fd027f43" +[[package]] +name = "bb8" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7c2093d15d6a1d33b1f972e1c5ea3177748742b97a5f392aa83a65262c6780" +dependencies = [ + "async-trait", + "futures-channel", + "futures-util", + "parking_lot", + "tokio", +] + [[package]] name = "bcrypt" version = "0.15.1" @@ -414,7 +430,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", "syn_derive", ] @@ -564,7 +580,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", + "futures-core", "memchr", + "pin-project-lite", + "tokio", + "tokio-util", ] [[package]] @@ -711,7 +731,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -896,9 +916,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" dependencies = [ "serde", ] @@ -1203,9 +1223,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "js-sys", @@ -1458,7 +1478,7 @@ checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -1472,9 +1492,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -1487,7 +1507,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -1622,9 +1642,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libfuzzer-sys" @@ -1688,9 +1708,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -1718,14 +1738,16 @@ dependencies = [ ] [[package]] -name = "macro_rs" +name = "macro-rs" version = "0.0.0" dependencies = [ "convert_case", "napi", "proc-macro2", "quote", - "syn 2.0.63", + "serde", + "serde_json", + "syn 2.0.64", "thiserror", ] @@ -1769,9 +1791,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", "simd-adler32", @@ -1790,14 +1812,12 @@ dependencies = [ [[package]] name = "napi" -version = "2.16.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc300228808a0e6aea5a58115c82889240bcf8dab16fc25ad675b33e454b368" +version = "3.0.0-alpha.2" +source = "git+https://github.com/napi-rs/napi-rs.git?rev=ca2cd5c35a0c39ec4a94e93c6c5695b681046df2#ca2cd5c35a0c39ec4a94e93c6c5695b681046df2" dependencies = [ "bitflags 2.5.0", "chrono", "ctor", - "napi-derive", "napi-sys", "once_cell", "serde", @@ -1813,23 +1833,23 @@ checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a" [[package]] name = "napi-derive" -version = "2.16.4" +version = "2.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb613535cde46cff231e53cd819c1694a32d48946bc2dda6b41174ace52ac08" +checksum = "e0e034ddf6155192cf83f267ede763fe6c164dfa9971585436b16173718d94c4" dependencies = [ "cfg-if", "convert_case", "napi-derive-backend", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] name = "napi-derive-backend" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da041b19246ab4240998774e987fd9a7d92cc7406b91b5eddb6691e81feac044" +checksum = "bff2c00437f3b3266391eb5e6aa25d0029187daf5caf05b8e3271468fb5ae73e" dependencies = [ "convert_case", "once_cell", @@ -1837,14 +1857,13 @@ dependencies = [ "quote", "regex", "semver", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] name = "napi-sys" version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3" +source = "git+https://github.com/napi-rs/napi-rs.git?rev=ca2cd5c35a0c39ec4a94e93c6c5695b681046df2#ca2cd5c35a0c39ec4a94e93c6c5695b681046df2" dependencies = [ "libloading", ] @@ -1966,7 +1985,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2058,7 +2077,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2119,7 +2138,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2277,7 +2296,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2459,7 +2478,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" dependencies = [ "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2618,12 +2637,16 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6472825949c09872e8f2c50bde59fcefc17748b6be5c90fd67cd8b4daca73bfd" dependencies = [ + "async-trait", + "bytes", "combine", + "futures-util", "itoa", "percent-encoding", + "pin-project-lite", "ryu", - "sha1_smol", - "socket2", + "tokio", + "tokio-util", "url", ] @@ -2921,7 +2944,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2962,7 +2985,7 @@ dependencies = [ "proc-macro2", "quote", "sea-bae", - "syn 2.0.63", + "syn 2.0.64", "unicode-ident", ] @@ -3053,7 +3076,7 @@ checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -3100,12 +3123,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - [[package]] name = "sha2" version = "0.10.8" @@ -3532,7 +3549,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -3554,9 +3571,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.63" +version = "2.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" dependencies = [ "proc-macro2", "quote", @@ -3572,7 +3589,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -3641,22 +3658,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -3753,7 +3770,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -3767,6 +3784,32 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-test" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" +dependencies = [ + "async-stream", + "bytes", + "futures-core", + "tokio", + "tokio-stream", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.8.13" @@ -3832,7 +3875,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -4046,7 +4089,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", "wasm-bindgen-shared", ] @@ -4068,7 +4111,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4082,7 +4125,7 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-push" version = "0.10.1" -source = "git+https://github.com/pimeys/rust-web-push?rev=40febe4085e3cef9cdfd539c315e3e945aba0656#40febe4085e3cef9cdfd539c315e3e945aba0656" +source = "git+https://github.com/pimeys/rust-web-push.git?rev=40febe4085e3cef9cdfd539c315e3e945aba0656#40febe4085e3cef9cdfd539c315e3e945aba0656" dependencies = [ "async-trait", "base64 0.13.1", @@ -4352,7 +4395,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ef32f6dc69..8549a1aa6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,14 +3,16 @@ members = ["packages/backend-rs", "packages/macro-rs"] resolver = "2" [workspace.dependencies] -macro_rs = { path = "packages/macro-rs" } +macro-rs = { path = "packages/macro-rs" } -napi = { version = "2.16.6", default-features = false } -napi-derive = "2.16.4" +napi = { git = "https://github.com/napi-rs/napi-rs.git", rev = "ca2cd5c35a0c39ec4a94e93c6c5695b681046df2", default-features = false } +napi-derive = "2.16.5" napi-build = "2.1.3" argon2 = "0.5.3" +async-trait = "0.1.80" basen = "0.1.0" +bb8 = "0.8.3" bcrypt = "0.15.1" chrono = "0.4.38" convert_case = "0.6.0" @@ -26,7 +28,7 @@ pretty_assertions = "1.4.0" proc-macro2 = "1.0.82" quote = "1.0.36" rand = "0.8.5" -redis = "0.25.3" +redis = { version = "0.25.3", default-features = false } regex = "1.10.4" rmp-serde = "1.3.0" sea-orm = "0.12.15" @@ -34,15 +36,16 @@ serde = "1.0.202" serde_json = "1.0.117" serde_yaml = "0.9.34" strum = "0.26.2" -syn = "2.0.63" +syn = "2.0.64" sysinfo = "0.30.12" -thiserror = "1.0.60" +thiserror = "1.0.61" tokio = "1.37.0" +tokio-test = "0.4.4" tracing = "0.1.40" tracing-subscriber = "0.3.18" url = "2.5.0" urlencoding = "2.1.3" -web-push = { git = "https://github.com/pimeys/rust-web-push", rev = "40febe4085e3cef9cdfd539c315e3e945aba0656" } +web-push = { git = "https://github.com/pimeys/rust-web-push.git", rev = "40febe4085e3cef9cdfd539c315e3e945aba0656" } [profile.release] lto = true diff --git a/docs/changelog.md b/docs/changelog.md index 19a93f7924..5b82648d84 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -5,6 +5,10 @@ Critical security updates are indicated by the :warning: icon. - Server administrators should check [notice-for-admins.md](./notice-for-admins.md) as well. - Third-party client/bot developers may want to check [api-change.md](./api-change.md) as well. +## Unreleased + +- Fix bugs + ## [v20240516](https://firefish.dev/firefish/firefish/-/merge_requests/10854/commits) - Improve timeline UX (you can restore the original appearance by settings) diff --git a/docs/downgrade.sql b/docs/downgrade.sql index 6691cce9f9..74cfa34e4f 100644 --- a/docs/downgrade.sql +++ b/docs/downgrade.sql @@ -2,6 +2,10 @@ BEGIN; DELETE FROM "migrations" WHERE name IN ( 'CreateScheduledNoteCreation1714728200194', + 'AddBackTimezone1715351290096', + 'UserprofileJsonbToArray1714270605574', + 'DropUnusedUserprofileColumns1714259023878', + 'AntennaJsonbToArray1714192520471', 'AddUserProfileLanguage1714888400293', 'DropUnusedIndexes1714643926317', 'AlterAkaType1714099399879', @@ -31,6 +35,45 @@ DELETE FROM "migrations" WHERE name IN ( -- create-scheduled-note-creation DROP TABLE "scheduled_note_creation"; +-- userprofile-jsonb-to-array +ALTER TABLE "user_profile" RENAME COLUMN "mutedInstances" TO "mutedInstances_old"; +ALTER TABLE "user_profile" ADD COLUMN "mutedInstances" jsonb NOT NULL DEFAULT '[]'; +UPDATE "user_profile" SET "mutedInstances" = to_jsonb("mutedInstances_old"); +ALTER TABLE "user_profile" DROP COLUMN "mutedInstances_old"; +ALTER TABLE "user_profile" RENAME COLUMN "mutedWords" TO "mutedWords_old"; +ALTER TABLE "user_profile" ADD COLUMN "mutedWords" jsonb NOT NULL DEFAULT '[]'; +CREATE TEMP TABLE "BCrsGgLCUeMMLARy" ("userId" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]'); +INSERT INTO "BCrsGgLCUeMMLARy" ("userId", "kws") SELECT "userId", jsonb_agg("X"."w") FROM (SELECT "userId", to_jsonb(string_to_array(unnest("mutedWords_old"), ' ')) AS "w" FROM "user_profile") AS "X" GROUP BY "userId"; +UPDATE "user_profile" SET "mutedWords" = "kws" FROM "BCrsGgLCUeMMLARy" WHERE "user_profile"."userId" = "BCrsGgLCUeMMLARy"."userId"; +ALTER TABLE "user_profile" DROP COLUMN "mutedWords_old"; + +-- drop-unused-userprofile-columns +ALTER TABLE "user_profile" ADD "room" jsonb NOT NULL DEFAULT '{}'; +COMMENT ON COLUMN "user_profile"."room" IS 'The room data of the User.'; +ALTER TABLE "user_profile" ADD "clientData" jsonb NOT NULL DEFAULT '{}'; +COMMENT ON COLUMN "user_profile"."clientData" IS 'The client-specific data of the User.'; + +-- antenna-jsonb-to-array +UPDATE "antenna" SET "instances" = '{""}' WHERE "instances" = '{}'; +ALTER TABLE "antenna" RENAME COLUMN "instances" TO "instances_old"; +ALTER TABLE "antenna" ADD COLUMN "instances" jsonb NOT NULL DEFAULT '[]'; +UPDATE "antenna" SET "instances" = to_jsonb("instances_old"); +ALTER TABLE "antenna" DROP COLUMN "instances_old"; +UPDATE "antenna" SET "keywords" = '{""}' WHERE "keywords" = '{}'; +ALTER TABLE "antenna" RENAME COLUMN "keywords" TO "keywords_old"; +ALTER TABLE "antenna" ADD COLUMN "keywords" jsonb NOT NULL DEFAULT '[]'; +CREATE TEMP TABLE "QvPNcMitBFkqqBgm" ("id" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]'); +INSERT INTO "QvPNcMitBFkqqBgm" ("id", "kws") SELECT "id", jsonb_agg("X"."w") FROM (SELECT "id", to_jsonb(string_to_array(unnest("keywords_old"), ' ')) AS "w" FROM "antenna") AS "X" GROUP BY "id"; +UPDATE "antenna" SET "keywords" = "kws" FROM "QvPNcMitBFkqqBgm" WHERE "antenna"."id" = "QvPNcMitBFkqqBgm"."id"; +ALTER TABLE "antenna" DROP COLUMN "keywords_old"; +UPDATE "antenna" SET "excludeKeywords" = '{""}' WHERE "excludeKeywords" = '{}'; +ALTER TABLE "antenna" RENAME COLUMN "excludeKeywords" TO "excludeKeywords_old"; +ALTER TABLE "antenna" ADD COLUMN "excludeKeywords" jsonb NOT NULL DEFAULT '[]'; +CREATE TEMP TABLE "MZvVSjHzYcGXmGmz" ("id" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]'); +INSERT INTO "MZvVSjHzYcGXmGmz" ("id", "kws") SELECT "id", jsonb_agg("X"."w") FROM (SELECT "id", to_jsonb(string_to_array(unnest("excludeKeywords_old"), ' ')) AS "w" FROM "antenna") AS "X" GROUP BY "id"; +UPDATE "antenna" SET "excludeKeywords" = "kws" FROM "MZvVSjHzYcGXmGmz" WHERE "antenna"."id" = "MZvVSjHzYcGXmGmz"."id"; +ALTER TABLE "antenna" DROP COLUMN "excludeKeywords_old"; + -- drop-unused-indexes CREATE INDEX "IDX_01f4581f114e0ebd2bbb876f0b" ON "note_reaction" ("createdAt"); CREATE INDEX "IDX_0610ebcfcfb4a18441a9bcdab2" ON "poll" ("userId"); @@ -67,83 +110,6 @@ CREATE INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f" ON "note" USING "pgroonga" ("cw" p ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_535def119223ac05ad3fa9ef64b"; ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_535def119223ac05ad3fa9ef64b" FOREIGN KEY ("fileId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION; --- drop-time-zone -ALTER TABLE "abuse_user_report" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "access_token" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "access_token" ALTER "lastUsedAt" TYPE timestamp with time zone; -ALTER TABLE "ad" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "ad" ALTER "expiresAt" TYPE timestamp with time zone; -ALTER TABLE "announcement" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "announcement" ALTER "updatedAt" TYPE timestamp with time zone; -ALTER TABLE "announcement_read" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "antenna" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "app" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "attestation_challenge" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "auth_session" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "blocking" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "channel" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "channel" ALTER "lastNotedAt" TYPE timestamp with time zone; -ALTER TABLE "channel_following" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "channel_note_pining" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "clip" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "drive_file" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "drive_folder" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "emoji" ALTER "updatedAt" TYPE timestamp with time zone; -ALTER TABLE "following" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "follow_request" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "gallery_like" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "gallery_post" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "gallery_post" ALTER "updatedAt" TYPE timestamp with time zone; -ALTER TABLE "instance" ALTER "caughtAt" TYPE timestamp with time zone; -ALTER TABLE "instance" ALTER "infoUpdatedAt" TYPE timestamp with time zone; -ALTER TABLE "instance" ALTER "lastCommunicatedAt" TYPE timestamp with time zone; -ALTER TABLE "instance" ALTER "latestRequestReceivedAt" TYPE timestamp with time zone; -ALTER TABLE "instance" ALTER "latestRequestSentAt" TYPE timestamp with time zone; -ALTER TABLE "messaging_message" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "moderation_log" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "muting" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "muting" ALTER "expiresAt" TYPE timestamp with time zone; -ALTER TABLE "note" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "note" ALTER "updatedAt" TYPE timestamp with time zone; -ALTER TABLE "note_edit" ALTER "updatedAt" TYPE timestamp with time zone; -ALTER TABLE "note_favorite" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "note_reaction" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "note_thread_muting" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "note_watching" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "notification" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "page" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "page" ALTER "updatedAt" TYPE timestamp with time zone; -ALTER TABLE "page_like" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "password_reset_request" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "poll" ALTER "expiresAt" TYPE timestamp with time zone; -ALTER TABLE "poll_vote" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "promo_note" ALTER "expiresAt" TYPE timestamp with time zone; -ALTER TABLE "promo_read" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "registration_ticket" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "registry_item" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "registry_item" ALTER "updatedAt" TYPE timestamp with time zone; -ALTER TABLE "renote_muting" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "reply_muting" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "signin" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "sw_subscription" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "used_username" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "user" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "user" ALTER "lastActiveDate" TYPE timestamp with time zone; -ALTER TABLE "user" ALTER "lastFetchedAt" TYPE timestamp with time zone; -ALTER TABLE "user" ALTER "updatedAt" TYPE timestamp with time zone; -ALTER TABLE "user_group" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "user_group_invitation" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "user_group_invite" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "user_group_joining" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "user_ip" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "user_list" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "user_list_joining" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "user_note_pining" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "user_pending" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "user_security_key" ALTER "lastUsed" TYPE timestamp with time zone; -ALTER TABLE "webhook" ALTER "createdAt" TYPE timestamp with time zone; -ALTER TABLE "webhook" ALTER "latestSentAt" TYPE timestamp with time zone; - -- expand-note-edit ALTER TABLE "note_edit" DROP COLUMN "emojis"; diff --git a/docs/notice-for-admins.md b/docs/notice-for-admins.md index 0ce284348c..5586e59076 100644 --- a/docs/notice-for-admins.md +++ b/docs/notice-for-admins.md @@ -2,6 +2,28 @@ You can skip intermediate versions when upgrading from an old version, but please read the notices and follow the instructions for each intermediate version before [upgrading](./upgrade.md). +## Unreleased + +### For all users + +We regret to inform you that the upgrade may take a long time to fix a regression we have introduced. The time required to upgrade should be the same as [v20240413](<https://firefish.dev/firefish/firefish/-/blob/main/docs/notice-for-admins.md#v20240413>). This is not a security fix, so please upgrade your server when you have enough time. We are sorry for the inconvenience. + +<details> + +There are two data types in PostgreSQL to store time: `timestamptz` (`timestamp with time zone`) and `timestamp` (`timestamp without time zone`) [[ref]](<https://www.postgresql.org/docs/current/datatype-datetime.html>). + +In Node.js, we manipulate the database using [TypeORM](<https://typeorm.io/>). TypeORM handles time data as a JavaScript `Date` object. Since `Date` doesn't have timezone information [[ref]](<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_components_and_time_zones>), we don't use the timezone information in the Node.js backend, and both `timestamptz` and `timestamp` behave in the same way. (Technically, the type names are a little confusing, and `timestamptz` (`timestamp with time zone`) doesn't store the timezone data. Please read PostgreSQL documentation for more information.) + +In Rust, we manipulate the database using [SeaORM](<https://www.sea-ql.org/SeaORM/>), which does distinguish between `timestamptz` and `timestamp`. `timestamptz` is converted to [`DateTime<FixedOffset>`](<https://docs.rs/chrono/latest/chrono/struct.DateTime.html>) type, whereas `timestamp` is converted to [`NaiveDateTime`](<https://docs.rs/chrono/latest/chrono/struct.NaiveDateTime.html>). + +We are using [napi-rs](<https://napi.rs/>) to implement some of the backend features in Rust, which did not support `DateTime<FixedOffset>`. We used to store time data as `timestamptz`, but we converted them to `timestamp` for this reason. As we don't use timezone data, we thought this was okay, and indeed it worked fine. + +However, we did not consider the case of migrating a server (hardware) to another timezone. With `timestamp`, there may be inconsistencies in the time data if you migrate your server to another system with a different timezone setting (Docker/Podman users should not be affected by this, as UTC is always used in containers unless you explicitly set one). + +Therefore, we have contributed to napi-rs to add support for `DateTime<FixedOffset>` (<https://github.com/napi-rs/napi-rs/pull/2074>) and decided to migrate back from `timestamp` to `timestamptz` to properly address this problem. The migration process takes time roughly proportional to the number of stored posts. + +</details> + ## v20240516 ### For all users diff --git a/packages/backend-rs/Cargo.toml b/packages/backend-rs/Cargo.toml index 0a307c1ef9..e11a0e0253 100644 --- a/packages/backend-rs/Cargo.toml +++ b/packages/backend-rs/Cargo.toml @@ -13,13 +13,15 @@ ci = [] crate-type = ["cdylib", "lib"] [dependencies] -macro_rs = { workspace = true } +macro-rs = { workspace = true } napi = { workspace = true, optional = true, default-features = false, features = ["napi9", "tokio_rt", "chrono_date", "serde-json"] } napi-derive = { workspace = true, optional = true } argon2 = { workspace = true, features = ["std"] } +async-trait = { workspace = true } basen = { workspace = true } +bb8 = { workspace = true } bcrypt = { workspace = true } chrono = { workspace = true } cuid2 = { workspace = true } @@ -31,7 +33,7 @@ nom-exif = { workspace = true } once_cell = { workspace = true } openssl = { workspace = true, features = ["vendored"] } rand = { workspace = true } -redis = { workspace = true } +redis = { workspace = true, default-features = false, features = ["streams", "tokio-comp"] } regex = { workspace = true } rmp-serde = { workspace = true } sea-orm = { workspace = true, features = ["sqlx-postgres", "runtime-tokio-rustls"] } @@ -50,6 +52,7 @@ web-push = { workspace = true } [dev-dependencies] pretty_assertions = { workspace = true } +tokio-test = { workspace = true } [build-dependencies] napi-build = { workspace = true } diff --git a/packages/backend-rs/build.rs b/packages/backend-rs/build.rs index 9fc2367889..9e5e97713c 100644 --- a/packages/backend-rs/build.rs +++ b/packages/backend-rs/build.rs @@ -1,5 +1,9 @@ extern crate napi_build; fn main() { + // watch the version in the project root package.json + println!("cargo:rerun-if-changed=../../package.json"); + + // napi napi_build::setup(); } diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts index 2199920310..fbd6c49403 100644 --- a/packages/backend-rs/index.d.ts +++ b/packages/backend-rs/index.d.ts @@ -191,18 +191,6 @@ export interface Config { authUrl: string driveUrl: string userAgent: string - clientEntry: Manifest -} -export interface Manifest { - file: string - name: string - src: string - isEntry: boolean - isDynamicEntry: boolean - imports: Array<string> - dynamicImports: Array<string> - css: Array<string> - assets: Array<string> } export function loadConfig(): Config export interface Acct { @@ -211,9 +199,9 @@ export interface Acct { } export function stringToAcct(acct: string): Acct export function acctToString(acct: Acct): string -export function showServerInfo(): void +export function greet(): void export function initializeRustLogger(): void -export function addNoteToAntenna(antennaId: string, note: Note): void +export function showServerInfo(): void /** * Checks if a server is blocked. * @@ -236,15 +224,7 @@ export function isSilencedServer(host: string): Promise<boolean> * `host` - punycoded instance host */ export function isAllowedServer(host: string): Promise<boolean> -export interface NoteLikeForCheckWordMute { - fileIds: Array<string> - userId: string | null - text: string | null - cw: string | null - renoteId: string | null - replyId: string | null -} -export function checkWordMute(note: NoteLikeForCheckWordMute, mutedWordLists: Array<Array<string>>, mutedPatterns: Array<string>): Promise<boolean> +export function checkWordMute(note: NoteLike, mutedWords: Array<string>, mutedPatterns: Array<string>): Promise<boolean> export function getFullApAccount(username: string, host?: string | undefined | null): string export function isSelfHost(host?: string | undefined | null): boolean export function isSameOrigin(uri: string): boolean @@ -260,6 +240,15 @@ export interface ImageSize { height: number } export function getImageSizeFromUrl(url: string): Promise<ImageSize> +/** TODO: handle name collisions better */ +export interface NoteLikeForAllTexts { + fileIds: Array<string> + userId: string + text: string | null + cw: string | null + renoteId: string | null + replyId: string | null +} export interface NoteLikeForGetNoteSummary { fileIds: Array<string> text: string | null @@ -267,28 +256,7 @@ export interface NoteLikeForGetNoteSummary { hasPoll: boolean } export function getNoteSummary(note: NoteLikeForGetNoteSummary): string -export interface Cpu { - model: string - cores: number -} -export interface Memory { - /** Total memory amount in bytes */ - total: number - /** Used memory amount in bytes */ - used: number - /** Available (for (re)use) memory amount in bytes */ - available: number -} -export interface Storage { - /** Total storage space in bytes */ - total: number - /** Used storage space in bytes */ - used: number -} -export function cpuInfo(): Cpu -export function cpuUsage(): number -export function memoryUsage(): Memory -export function storageUsage(): Storage | null +export function isQuote(note: Note): boolean export function isSafeUrl(url: string): boolean export function latestVersion(): Promise<string> export function toMastodonId(firefishId: string): string | null @@ -320,9 +288,31 @@ export function countReactions(reactions: Record<string, number>): Record<string export function toDbReaction(reaction?: string | undefined | null, host?: string | undefined | null): Promise<string> /** Delete all entries in the "attestation_challenge" table created at more than 5 minutes ago */ export function removeOldAttestationChallenges(): Promise<void> +export interface Cpu { + model: string + cores: number +} +export interface Memory { + /** Total memory amount in bytes */ + total: number + /** Used memory amount in bytes */ + used: number + /** Available (for (re)use) memory amount in bytes */ + available: number +} +export interface Storage { + /** Total storage space in bytes */ + total: number + /** Used storage space in bytes */ + used: number +} +export function cpuInfo(): Cpu +export function cpuUsage(): number +export function memoryUsage(): Memory +export function storageUsage(): Storage | null export interface AbuseUserReport { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone targetUserId: string reporterId: string assigneeId: string | null @@ -334,12 +324,12 @@ export interface AbuseUserReport { } export interface AccessToken { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone token: string hash: string userId: string appId: string | null - lastUsedAt: Date | null + lastUsedAt: DateTimeWithTimeZone | null session: string | null name: string | null description: string | null @@ -349,8 +339,8 @@ export interface AccessToken { } export interface Ad { id: string - createdAt: Date - expiresAt: Date + createdAt: DateTimeWithTimeZone + expiresAt: DateTimeWithTimeZone place: string priority: string url: string @@ -360,11 +350,11 @@ export interface Ad { } export interface Announcement { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone text: string title: string imageUrl: string | null - updatedAt: Date | null + updatedAt: DateTimeWithTimeZone | null showPopup: boolean isGoodNews: boolean } @@ -372,16 +362,15 @@ export interface AnnouncementRead { id: string userId: string announcementId: string - createdAt: Date + createdAt: DateTimeWithTimeZone } export interface Antenna { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string name: string src: AntennaSrcEnum userListId: string | null - keywords: Json withFile: boolean expression: string | null notify: boolean @@ -389,12 +378,13 @@ export interface Antenna { withReplies: boolean userGroupJoiningId: string | null users: Array<string> - excludeKeywords: Json - instances: Json + instances: Array<string> + keywords: Array<string> + excludeKeywords: Array<string> } export interface App { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string | null secret: string name: string @@ -406,26 +396,26 @@ export interface AttestationChallenge { id: string userId: string challenge: string - createdAt: Date + createdAt: DateTimeWithTimeZone registrationChallenge: boolean } export interface AuthSession { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone token: string userId: string | null appId: string } export interface Blocking { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone blockeeId: string blockerId: string } export interface Channel { id: string - createdAt: Date - lastNotedAt: Date | null + createdAt: DateTimeWithTimeZone + lastNotedAt: DateTimeWithTimeZone | null userId: string | null name: string description: string | null @@ -435,19 +425,19 @@ export interface Channel { } export interface ChannelFollowing { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone followeeId: string followerId: string } export interface ChannelNotePining { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone channelId: string noteId: string } export interface Clip { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string name: string isPublic: boolean @@ -460,7 +450,7 @@ export interface ClipNote { } export interface DriveFile { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string | null userHost: string | null md5: string @@ -489,14 +479,14 @@ export interface DriveFile { } export interface DriveFolder { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone name: string userId: string | null parentId: string | null } export interface Emoji { id: string - updatedAt: Date | null + updatedAt: DateTimeWithTimeZone | null name: string host: string | null originalUrl: string @@ -511,7 +501,7 @@ export interface Emoji { } export interface FollowRequest { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone followeeId: string followerId: string requestId: string | null @@ -524,7 +514,7 @@ export interface FollowRequest { } export interface Following { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone followeeId: string followerId: string followerHost: string | null @@ -536,14 +526,14 @@ export interface Following { } export interface GalleryLike { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string postId: string } export interface GalleryPost { id: string - createdAt: Date - updatedAt: Date + createdAt: DateTimeWithTimeZone + updatedAt: DateTimeWithTimeZone title: string description: string | null userId: string @@ -570,16 +560,16 @@ export interface Hashtag { } export interface Instance { id: string - caughtAt: Date + caughtAt: DateTimeWithTimeZone host: string usersCount: number notesCount: number followingCount: number followersCount: number - latestRequestSentAt: Date | null + latestRequestSentAt: DateTimeWithTimeZone | null latestStatus: number | null - latestRequestReceivedAt: Date | null - lastCommunicatedAt: Date + latestRequestReceivedAt: DateTimeWithTimeZone | null + lastCommunicatedAt: DateTimeWithTimeZone isNotResponding: boolean softwareName: string | null softwareVersion: string | null @@ -588,7 +578,7 @@ export interface Instance { description: string | null maintainerName: string | null maintainerEmail: string | null - infoUpdatedAt: Date | null + infoUpdatedAt: DateTimeWithTimeZone | null isSuspended: boolean iconUrl: string | null themeColor: string | null @@ -596,7 +586,7 @@ export interface Instance { } export interface MessagingMessage { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string recipientId: string | null text: string | null @@ -700,7 +690,7 @@ export interface Migrations { } export interface ModerationLog { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string type: string info: Json @@ -713,14 +703,14 @@ export interface MutedNote { } export interface Muting { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone muteeId: string muterId: string - expiresAt: Date | null + expiresAt: DateTimeWithTimeZone | null } export interface Note { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone replyId: string | null renoteId: string | null text: string | null @@ -750,7 +740,7 @@ export interface Note { url: string | null channelId: string | null threadId: string | null - updatedAt: Date | null + updatedAt: DateTimeWithTimeZone | null lang: string | null } export interface NoteEdit { @@ -759,12 +749,12 @@ export interface NoteEdit { text: string | null cw: string | null fileIds: Array<string> - updatedAt: Date + updatedAt: DateTimeWithTimeZone emojis: Array<string> } export interface NoteFavorite { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string noteId: string } @@ -775,14 +765,14 @@ export interface NoteFile { } export interface NoteReaction { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string noteId: string reaction: string } export interface NoteThreadMuting { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string threadId: string } @@ -797,14 +787,14 @@ export interface NoteUnread { } export interface NoteWatching { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string noteId: string noteUserId: string } export interface Notification { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone notifieeId: string notifierId: string | null isRead: boolean @@ -821,8 +811,8 @@ export interface Notification { } export interface Page { id: string - createdAt: Date - updatedAt: Date + createdAt: DateTimeWithTimeZone + updatedAt: DateTimeWithTimeZone title: string name: string summary: string | null @@ -841,19 +831,19 @@ export interface Page { } export interface PageLike { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string pageId: string } export interface PasswordResetRequest { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone token: string userId: string } export interface Poll { noteId: string - expiresAt: Date | null + expiresAt: DateTimeWithTimeZone | null multiple: boolean choices: Array<string> votes: Array<number> @@ -863,31 +853,31 @@ export interface Poll { } export interface PollVote { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string noteId: string choice: number } export interface PromoNote { noteId: string - expiresAt: Date + expiresAt: DateTimeWithTimeZone userId: string } export interface PromoRead { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string noteId: string } export interface RegistrationTicket { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone code: string } export interface RegistryItem { id: string - createdAt: Date - updatedAt: Date + createdAt: DateTimeWithTimeZone + updatedAt: DateTimeWithTimeZone userId: string key: string scope: Array<string> @@ -901,13 +891,13 @@ export interface Relay { } export interface RenoteMuting { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone muteeId: string muterId: string } export interface ReplyMuting { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone muteeId: string muterId: string } @@ -993,7 +983,7 @@ export enum UserProfileMutingnotificationtypesEnum { } export interface Signin { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string ip: string headers: Json @@ -1001,7 +991,7 @@ export interface Signin { } export interface SwSubscription { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string endpoint: string auth: string @@ -1010,13 +1000,13 @@ export interface SwSubscription { } export interface UsedUsername { username: string - createdAt: Date + createdAt: DateTimeWithTimeZone } export interface User { id: string - createdAt: Date - updatedAt: Date | null - lastFetchedAt: Date | null + createdAt: DateTimeWithTimeZone + updatedAt: DateTimeWithTimeZone | null + lastFetchedAt: DateTimeWithTimeZone | null username: string usernameLower: string name: string | null @@ -1042,7 +1032,7 @@ export interface User { token: string | null isExplorable: boolean followersUri: string | null - lastActiveDate: Date | null + lastActiveDate: DateTimeWithTimeZone | null hideOnlineStatus: boolean isDeleted: boolean driveCapacityOverrideMb: number | null @@ -1054,32 +1044,32 @@ export interface User { } export interface UserGroup { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone name: string userId: string isPrivate: boolean } export interface UserGroupInvitation { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string userGroupId: string } export interface UserGroupInvite { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string userGroupId: string } export interface UserGroupJoining { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string userGroupId: string } export interface UserIp { id: number - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string ip: string } @@ -1090,25 +1080,25 @@ export interface UserKeypair { } export interface UserList { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string name: string } export interface UserListJoining { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string userListId: string } export interface UserNotePining { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string noteId: string } export interface UserPending { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone code: string username: string email: string @@ -1128,7 +1118,6 @@ export interface UserProfile { twoFactorSecret: string | null twoFactorEnabled: boolean password: string | null - clientData: Json autoAcceptFollowed: boolean alwaysMarkNsfw: boolean carefulBot: boolean @@ -1136,21 +1125,20 @@ export interface UserProfile { securityKeysAvailable: boolean usePasswordLessLogin: boolean pinnedPageId: string | null - room: Json injectFeaturedNote: boolean enableWordMute: boolean - mutedWords: Json mutingNotificationTypes: Array<UserProfileMutingnotificationtypesEnum> noCrawle: boolean receiveAnnouncementEmail: boolean emailNotificationTypes: Json - mutedInstances: Json publicReactions: boolean ffVisibility: UserProfileFfvisibilityEnum moderationNote: string preventAiLearning: boolean isIndexable: boolean mutedPatterns: Array<string> + mutedInstances: Array<string> + mutedWords: Array<string> lang: string | null } export interface UserPublickey { @@ -1162,21 +1150,22 @@ export interface UserSecurityKey { id: string userId: string publicKey: string - lastUsed: Date + lastUsed: DateTimeWithTimeZone name: string } export interface Webhook { id: string - createdAt: Date + createdAt: DateTimeWithTimeZone userId: string name: string on: Array<string> url: string secret: string active: boolean - latestSentAt: Date | null + latestSentAt: DateTimeWithTimeZone | null latestStatus: number | null } +export function updateAntennasOnNewNote(note: Note, noteAuthor: Acct, noteMutedUsers: Array<string>): Promise<void> export function fetchNodeinfo(host: string): Promise<Nodeinfo> export function nodeinfo_2_1(): Promise<any> export function nodeinfo_2_0(): Promise<any> @@ -1285,22 +1274,23 @@ export enum PushNotificationKind { ReadAllChats = 'readAllChats', ReadAllChatsInTheRoom = 'readAllChatsInTheRoom', ReadNotifications = 'readNotifications', - ReadAllNotifications = 'readAllNotifications' + ReadAllNotifications = 'readAllNotifications', + Mastodon = 'mastodon' } export function sendPushNotification(receiverUserId: string, kind: PushNotificationKind, content: any): Promise<void> -export function publishToChannelStream(channelId: string, userId: string): void +export function publishToChannelStream(channelId: string, userId: string): Promise<void> export enum ChatEvent { Message = 'message', Read = 'read', Deleted = 'deleted', Typing = 'typing' } -export function publishToChatStream(senderUserId: string, receiverUserId: string, kind: ChatEvent, object: any): void +export function publishToChatStream(senderUserId: string, receiverUserId: string, kind: ChatEvent, object: any): Promise<void> export enum ChatIndexEvent { Message = 'message', Read = 'read' } -export function publishToChatIndexStream(userId: string, kind: ChatIndexEvent, object: any): void +export function publishToChatIndexStream(userId: string, kind: ChatIndexEvent, object: any): Promise<void> export interface PackedEmoji { id: string aliases: Array<string> @@ -1312,15 +1302,15 @@ export interface PackedEmoji { width: number | null height: number | null } -export function publishToBroadcastStream(emoji: PackedEmoji): void -export function publishToGroupChatStream(groupId: string, kind: ChatEvent, object: any): void +export function publishToBroadcastStream(emoji: PackedEmoji): Promise<void> +export function publishToGroupChatStream(groupId: string, kind: ChatEvent, object: any): Promise<void> export interface AbuseUserReportLike { id: string targetUserId: string reporterId: string comment: string } -export function publishToModerationStream(moderatorId: string, report: AbuseUserReportLike): void +export function publishToModerationStream(moderatorId: string, report: AbuseUserReportLike): Promise<void> export function getTimestamp(id: string): number /** * The generated ID results in the form of `[8 chars timestamp] + [cuid2]`. diff --git a/packages/backend-rs/index.js b/packages/backend-rs/index.js index 36cd88dcbd..0dc930ed2c 100644 --- a/packages/backend-rs/index.js +++ b/packages/backend-rs/index.js @@ -310,7 +310,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, showServerInfo, initializeRustLogger, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, cpuInfo, cpuUsage, memoryUsage, storageUsage, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, Protocol, Inbound, Outbound, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding +const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, greet, initializeRustLogger, showServerInfo, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isQuote, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, updateAntennasOnNewNote, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, Protocol, Inbound, Outbound, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding module.exports.SECOND = SECOND module.exports.MINUTE = MINUTE @@ -323,9 +323,9 @@ module.exports.loadEnv = loadEnv module.exports.loadConfig = loadConfig module.exports.stringToAcct = stringToAcct module.exports.acctToString = acctToString -module.exports.showServerInfo = showServerInfo +module.exports.greet = greet module.exports.initializeRustLogger = initializeRustLogger -module.exports.addNoteToAntenna = addNoteToAntenna +module.exports.showServerInfo = showServerInfo module.exports.isBlockedServer = isBlockedServer module.exports.isSilencedServer = isSilencedServer module.exports.isAllowedServer = isAllowedServer @@ -341,10 +341,7 @@ module.exports.safeForSql = safeForSql module.exports.formatMilliseconds = formatMilliseconds module.exports.getImageSizeFromUrl = getImageSizeFromUrl module.exports.getNoteSummary = getNoteSummary -module.exports.cpuInfo = cpuInfo -module.exports.cpuUsage = cpuUsage -module.exports.memoryUsage = memoryUsage -module.exports.storageUsage = storageUsage +module.exports.isQuote = isQuote module.exports.isSafeUrl = isSafeUrl module.exports.latestVersion = latestVersion module.exports.toMastodonId = toMastodonId @@ -359,6 +356,10 @@ module.exports.decodeReaction = decodeReaction module.exports.countReactions = countReactions module.exports.toDbReaction = toDbReaction module.exports.removeOldAttestationChallenges = removeOldAttestationChallenges +module.exports.cpuInfo = cpuInfo +module.exports.cpuUsage = cpuUsage +module.exports.memoryUsage = memoryUsage +module.exports.storageUsage = storageUsage module.exports.AntennaSrcEnum = AntennaSrcEnum module.exports.DriveFileUsageHintEnum = DriveFileUsageHintEnum module.exports.MutedNoteReasonEnum = MutedNoteReasonEnum @@ -370,6 +371,7 @@ module.exports.RelayStatusEnum = RelayStatusEnum module.exports.UserEmojimodpermEnum = UserEmojimodpermEnum module.exports.UserProfileFfvisibilityEnum = UserProfileFfvisibilityEnum module.exports.UserProfileMutingnotificationtypesEnum = UserProfileMutingnotificationtypesEnum +module.exports.updateAntennasOnNewNote = updateAntennasOnNewNote module.exports.fetchNodeinfo = fetchNodeinfo module.exports.nodeinfo_2_1 = nodeinfo_2_1 module.exports.nodeinfo_2_0 = nodeinfo_2_0 diff --git a/packages/backend-rs/src/config/server.rs b/packages/backend-rs/src/config/server.rs index 52ff2e7707..838516af04 100644 --- a/packages/backend-rs/src/config/server.rs +++ b/packages/backend-rs/src/config/server.rs @@ -3,6 +3,8 @@ use serde::Deserialize; use std::env; use std::fs; +pub const VERSION: &str = macro_rs::read_version_from_package_json!(); + #[derive(Clone, Debug, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] #[crate::export(object, use_nullable = false)] @@ -231,34 +233,6 @@ pub struct Config { pub auth_url: String, pub drive_url: String, pub user_agent: String, - pub client_entry: Manifest, -} - -#[derive(Clone, Debug, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -struct Meta { - pub version: String, -} - -#[derive(Clone, Debug, PartialEq, Deserialize)] -struct ManifestJson { - #[serde(rename = "src/init.ts")] - pub init_ts: Manifest, -} - -#[derive(Clone, Debug, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -#[crate::export(object, use_nullable = false)] -pub struct Manifest { - pub file: String, - pub name: String, - pub src: String, - pub is_entry: bool, - pub is_dynamic_entry: bool, - pub imports: Vec<String>, - pub dynamic_imports: Vec<String>, - pub css: Vec<String>, - pub assets: Vec<String>, } fn read_config_file() -> ServerConfig { @@ -280,28 +254,10 @@ fn read_config_file() -> ServerConfig { data } -fn read_meta() -> Meta { - let cwd = env::current_dir().unwrap(); - let meta_json = fs::File::open(cwd.join("../../built/meta.json")) - .expect("Failed to open 'built/meta.json'"); - serde_json::from_reader(meta_json).expect("Failed to parse built/meta.json") -} - -fn read_manifest() -> Manifest { - let cwd = env::current_dir().unwrap(); - let manifest_json = fs::File::open(cwd.join("../../built/_client_dist_/manifest.json")) - .expect("Failed to open 'built/_client_dist_/manifest.json'"); - let manifest: ManifestJson = serde_json::from_reader(manifest_json) - .expect("Failed to parse built/_client_dist_/manifest.json"); - - manifest.init_ts -} - #[crate::export] pub fn load_config() -> Config { let server_config = read_config_file(); - let version = read_meta().version; - let manifest = read_manifest(); + let version = VERSION.to_owned(); let url = url::Url::parse(&server_config.url).expect("Config url is invalid"); let hostname = url .host_str() @@ -379,7 +335,6 @@ pub fn load_config() -> Config { redis_key_prefix, scheme, ws_scheme, - client_entry: manifest, } } diff --git a/packages/backend-rs/src/database/cache.rs b/packages/backend-rs/src/database/cache.rs index ba2910f254..dd0509a31a 100644 --- a/packages/backend-rs/src/database/cache.rs +++ b/packages/backend-rs/src/database/cache.rs @@ -1,11 +1,15 @@ -use crate::database::{redis_conn, redis_key}; -use redis::{Commands, RedisError}; +use crate::database::{redis_conn, redis_key, RedisConnError}; +use redis::{AsyncCommands, RedisError}; use serde::{Deserialize, Serialize}; #[derive(strum::Display, Debug)] pub enum Category { #[strum(serialize = "fetchUrl")] FetchUrl, + #[strum(serialize = "blocking")] + Block, + #[strum(serialize = "following")] + Follow, #[cfg(test)] #[strum(serialize = "usedOnlyForTesting")] Test, @@ -14,11 +18,13 @@ pub enum Category { #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Redis error: {0}")] - RedisError(#[from] RedisError), + RedisErr(#[from] RedisError), + #[error("Redis connection error: {0}")] + RedisConnErr(#[from] RedisConnError), #[error("Data serialization error: {0}")] - SerializeError(#[from] rmp_serde::encode::Error), + SerializeErr(#[from] rmp_serde::encode::Error), #[error("Data deserialization error: {0}")] - DeserializeError(#[from] rmp_serde::decode::Error), + DeserializeErr(#[from] rmp_serde::decode::Error), } #[inline] @@ -50,26 +56,31 @@ fn wildcard(category: Category) -> String { /// /// ``` /// # use backend_rs::database::cache; +/// # tokio_test::block_on(async { /// let key = "apple"; /// let data = "I want to cache this string".to_string(); /// /// // caches the data for 10 seconds -/// cache::set(key, &data, 10); +/// cache::set(key, &data, 10).await; /// /// // get the cache -/// let cached_data = cache::get::<String>(key).unwrap(); +/// let cached_data = cache::get::<String>(key).await.unwrap(); /// assert_eq!(data, cached_data.unwrap()); +/// # }) /// ``` -pub fn set<V: for<'a> Deserialize<'a> + Serialize>( +pub async fn set<V: for<'a> Deserialize<'a> + Serialize>( key: &str, value: &V, expire_seconds: u64, ) -> Result<(), Error> { - redis_conn()?.set_ex( - prefix_key(key), - rmp_serde::encode::to_vec(&value)?, - expire_seconds, - )?; + redis_conn() + .await? + .set_ex( + prefix_key(key), + rmp_serde::encode::to_vec(&value)?, + expire_seconds, + ) + .await?; Ok(()) } @@ -86,22 +97,24 @@ pub fn set<V: for<'a> Deserialize<'a> + Serialize>( /// /// ``` /// # use backend_rs::database::cache; +/// # tokio_test::block_on(async { /// let key = "banana"; /// let data = "I want to cache this string".to_string(); /// /// // set cache -/// cache::set(key, &data, 10).unwrap(); +/// cache::set(key, &data, 10).await.unwrap(); /// /// // get cache -/// let cached_data = cache::get::<String>(key).unwrap(); +/// let cached_data = cache::get::<String>(key).await.unwrap(); /// assert_eq!(data, cached_data.unwrap()); /// /// // get nonexistent (or expired) cache -/// let no_cache = cache::get::<String>("nonexistent").unwrap(); +/// let no_cache = cache::get::<String>("nonexistent").await.unwrap(); /// assert!(no_cache.is_none()); +/// # }) /// ``` -pub fn get<V: for<'a> Deserialize<'a> + Serialize>(key: &str) -> Result<Option<V>, Error> { - let serialized_value: Option<Vec<u8>> = redis_conn()?.get(prefix_key(key))?; +pub async fn get<V: for<'a> Deserialize<'a> + Serialize>(key: &str) -> Result<Option<V>, Error> { + let serialized_value: Option<Vec<u8>> = redis_conn().await?.get(prefix_key(key)).await?; Ok(match serialized_value { Some(v) => Some(rmp_serde::from_slice::<V>(v.as_ref())?), None => None, @@ -121,22 +134,24 @@ pub fn get<V: for<'a> Deserialize<'a> + Serialize>(key: &str) -> Result<Option<V /// /// ``` /// # use backend_rs::database::cache; +/// # tokio_test::block_on(async { /// let key = "chocolate"; /// let value = "I want to cache this string".to_string(); /// /// // set cache -/// cache::set(key, &value, 10).unwrap(); +/// cache::set(key, &value, 10).await.unwrap(); /// /// // delete the cache -/// cache::delete("foo").unwrap(); -/// cache::delete("nonexistent").unwrap(); // this is okay +/// cache::delete("foo").await.unwrap(); +/// cache::delete("nonexistent").await.unwrap(); // this is okay /// /// // the cache is gone -/// let cached_value = cache::get::<String>("foo").unwrap(); +/// let cached_value = cache::get::<String>("foo").await.unwrap(); /// assert!(cached_value.is_none()); +/// # }) /// ``` -pub fn delete(key: &str) -> Result<(), Error> { - Ok(redis_conn()?.del(prefix_key(key))?) +pub async fn delete(key: &str) -> Result<(), Error> { + Ok(redis_conn().await?.del(prefix_key(key)).await?) } /// Sets a Redis cache under a `category`. @@ -150,13 +165,13 @@ pub fn delete(key: &str) -> Result<(), Error> { /// * `key` - key (will be prefixed automatically) /// * `value` - (de)serializable value /// * `expire_seconds` - TTL -pub fn set_one<V: for<'a> Deserialize<'a> + Serialize>( +pub async fn set_one<V: for<'a> Deserialize<'a> + Serialize>( category: Category, key: &str, value: &V, expire_seconds: u64, ) -> Result<(), Error> { - set(&categorize(category, key), value, expire_seconds) + set(&categorize(category, key), value, expire_seconds).await } /// Gets a Redis cache under a `category`. @@ -167,11 +182,11 @@ pub fn set_one<V: for<'a> Deserialize<'a> + Serialize>( /// /// * `category` - one of [Category] /// * `key` - key (will be prefixed automatically) -pub fn get_one<V: for<'a> Deserialize<'a> + Serialize>( +pub async fn get_one<V: for<'a> Deserialize<'a> + Serialize>( category: Category, key: &str, ) -> Result<Option<V>, Error> { - get(&categorize(category, key)) + get(&categorize(category, key)).await } /// Deletes a Redis cache under a `category`. @@ -182,8 +197,8 @@ pub fn get_one<V: for<'a> Deserialize<'a> + Serialize>( /// /// * `category` - one of [Category] /// * `key` - key (will be prefixed automatically) -pub fn delete_one(category: Category, key: &str) -> Result<(), Error> { - delete(&categorize(category, key)) +pub async fn delete_one(category: Category, key: &str) -> Result<(), Error> { + delete(&categorize(category, key)).await } /// Deletes all Redis caches under a `category`. @@ -191,28 +206,27 @@ pub fn delete_one(category: Category, key: &str) -> Result<(), Error> { /// ## Arguments /// /// * `category` - one of [Category] -pub fn delete_all(category: Category) -> Result<(), Error> { - let mut redis = redis_conn()?; - let keys: Vec<Vec<u8>> = redis.keys(wildcard(category))?; +pub async fn delete_all(category: Category) -> Result<(), Error> { + let mut redis = redis_conn().await?; + let keys: Vec<Vec<u8>> = redis.keys(wildcard(category)).await?; if !keys.is_empty() { - redis.del(keys)? + redis.del(keys).await? } Ok(()) } -// TODO: set_all(), get_all() +// TODO: get_all() #[cfg(test)] mod unit_test { - use crate::database::cache::delete_one; - use super::{delete_all, get, get_one, set, set_one, Category::Test}; + use crate::database::cache::delete_one; use pretty_assertions::assert_eq; - #[test] - fn set_get_expire() { + #[tokio::test] + async fn set_get_expire() { #[derive(serde::Deserialize, serde::Serialize, PartialEq, Debug)] struct Data { id: u32, @@ -231,13 +245,13 @@ mod unit_test { kind: "prime number".to_string(), }; - set(key_1, &value_1, 1).unwrap(); - set(key_2, &value_2, 1).unwrap(); - set(key_3, &value_3, 1).unwrap(); + set(key_1, &value_1, 1).await.unwrap(); + set(key_2, &value_2, 1).await.unwrap(); + set(key_3, &value_3, 1).await.unwrap(); - let cached_value_1: Vec<i32> = get(key_1).unwrap().unwrap(); - let cached_value_2: String = get(key_2).unwrap().unwrap(); - let cached_value_3: Data = get(key_3).unwrap().unwrap(); + let cached_value_1: Vec<i32> = get(key_1).await.unwrap().unwrap(); + let cached_value_2: String = get(key_2).await.unwrap().unwrap(); + let cached_value_3: Data = get(key_3).await.unwrap().unwrap(); assert_eq!(value_1, cached_value_1); assert_eq!(value_2, cached_value_2); @@ -246,17 +260,17 @@ mod unit_test { // wait for the cache to expire std::thread::sleep(std::time::Duration::from_millis(1100)); - let expired_value_1: Option<Vec<i32>> = get(key_1).unwrap(); - let expired_value_2: Option<Vec<i32>> = get(key_2).unwrap(); - let expired_value_3: Option<Vec<i32>> = get(key_3).unwrap(); + let expired_value_1: Option<Vec<i32>> = get(key_1).await.unwrap(); + let expired_value_2: Option<Vec<i32>> = get(key_2).await.unwrap(); + let expired_value_3: Option<Vec<i32>> = get(key_3).await.unwrap(); assert!(expired_value_1.is_none()); assert!(expired_value_2.is_none()); assert!(expired_value_3.is_none()); } - #[test] - fn use_category() { + #[tokio::test] + async fn use_category() { let key_1 = "fire"; let key_2 = "fish"; let key_3 = "awawa"; @@ -265,24 +279,30 @@ mod unit_test { let value_2 = 998244353u32; let value_3 = 'あ'; - set_one(Test, key_1, &value_1, 5 * 60).unwrap(); - set_one(Test, key_2, &value_2, 5 * 60).unwrap(); - set_one(Test, key_3, &value_3, 5 * 60).unwrap(); + set_one(Test, key_1, &value_1, 5 * 60).await.unwrap(); + set_one(Test, key_2, &value_2, 5 * 60).await.unwrap(); + set_one(Test, key_3, &value_3, 5 * 60).await.unwrap(); - assert_eq!(get_one::<String>(Test, key_1).unwrap().unwrap(), value_1); - assert_eq!(get_one::<u32>(Test, key_2).unwrap().unwrap(), value_2); - assert_eq!(get_one::<char>(Test, key_3).unwrap().unwrap(), value_3); + assert_eq!( + get_one::<String>(Test, key_1).await.unwrap().unwrap(), + value_1 + ); + assert_eq!(get_one::<u32>(Test, key_2).await.unwrap().unwrap(), value_2); + assert_eq!( + get_one::<char>(Test, key_3).await.unwrap().unwrap(), + value_3 + ); - delete_one(Test, key_1).unwrap(); + delete_one(Test, key_1).await.unwrap(); - assert!(get_one::<String>(Test, key_1).unwrap().is_none()); - assert!(get_one::<u32>(Test, key_2).unwrap().is_some()); - assert!(get_one::<char>(Test, key_3).unwrap().is_some()); + assert!(get_one::<String>(Test, key_1).await.unwrap().is_none()); + assert!(get_one::<u32>(Test, key_2).await.unwrap().is_some()); + assert!(get_one::<char>(Test, key_3).await.unwrap().is_some()); - delete_all(Test).unwrap(); + delete_all(Test).await.unwrap(); - assert!(get_one::<String>(Test, key_1).unwrap().is_none()); - assert!(get_one::<u32>(Test, key_2).unwrap().is_none()); - assert!(get_one::<char>(Test, key_3).unwrap().is_none()); + assert!(get_one::<String>(Test, key_1).await.unwrap().is_none()); + assert!(get_one::<u32>(Test, key_2).await.unwrap().is_none()); + assert!(get_one::<char>(Test, key_3).await.unwrap().is_none()); } } diff --git a/packages/backend-rs/src/database/mod.rs b/packages/backend-rs/src/database/mod.rs index f657a540af..428bc17435 100644 --- a/packages/backend-rs/src/database/mod.rs +++ b/packages/backend-rs/src/database/mod.rs @@ -1,6 +1,7 @@ pub use postgresql::db_conn; pub use redis::key as redis_key; pub use redis::redis_conn; +pub use redis::RedisConnError; pub mod cache; pub mod postgresql; diff --git a/packages/backend-rs/src/database/redis.rs b/packages/backend-rs/src/database/redis.rs index b049a75545..4290c4c1b6 100644 --- a/packages/backend-rs/src/database/redis.rs +++ b/packages/backend-rs/src/database/redis.rs @@ -1,10 +1,50 @@ use crate::config::CONFIG; -use once_cell::sync::OnceCell; -use redis::{Client, Connection, RedisError}; +use async_trait::async_trait; +use bb8::{ManageConnection, Pool, PooledConnection, RunError}; +use redis::{aio::MultiplexedConnection, Client, ErrorKind, IntoConnectionInfo, RedisError}; +use tokio::sync::OnceCell; -static REDIS_CLIENT: OnceCell<Client> = OnceCell::new(); +/// A `bb8::ManageConnection` for `redis::Client::get_multiplexed_async_connection`. +#[derive(Clone, Debug)] +pub struct RedisConnectionManager { + client: Client, +} -fn init_redis() -> Result<Client, RedisError> { +impl RedisConnectionManager { + /// Create a new `RedisConnectionManager`. + /// See `redis::Client::open` for a description of the parameter types. + pub fn new<T: IntoConnectionInfo>(info: T) -> Result<Self, RedisError> { + Ok(Self { + client: Client::open(info.into_connection_info()?)?, + }) + } +} + +#[async_trait] +impl ManageConnection for RedisConnectionManager { + type Connection = MultiplexedConnection; + type Error = RedisError; + + async fn connect(&self) -> Result<Self::Connection, Self::Error> { + self.client.get_multiplexed_async_connection().await + } + + async fn is_valid(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> { + let pong: String = redis::cmd("PING").query_async(conn).await?; + match pong.as_str() { + "PONG" => Ok(()), + _ => Err((ErrorKind::ResponseError, "ping request").into()), + } + } + + fn has_broken(&self, _: &mut Self::Connection) -> bool { + false + } +} + +static CONN_POOL: OnceCell<Pool<RedisConnectionManager>> = OnceCell::const_new(); + +async fn init_conn_pool() -> Result<(), RedisError> { let redis_url = { let mut params = vec!["redis://".to_owned()]; @@ -27,16 +67,40 @@ fn init_redis() -> Result<Client, RedisError> { params.concat() }; - tracing::info!("Initializing Redis client"); + tracing::info!("Initializing connection manager"); + let manager = RedisConnectionManager::new(redis_url)?; - Client::open(redis_url) + tracing::info!("Creating connection pool"); + let pool = Pool::builder().build(manager).await?; + + CONN_POOL.get_or_init(|| async { pool }).await; + Ok(()) } -pub fn redis_conn() -> Result<Connection, RedisError> { - match REDIS_CLIENT.get() { - Some(client) => Ok(client.get_connection()?), - None => init_redis()?.get_connection(), +#[derive(thiserror::Error, Debug)] +pub enum RedisConnError { + #[error("Failed to initialize Redis connection pool: {0}")] + RedisErr(RedisError), + #[error("Redis connection pool error: {0}")] + Bb8PoolErr(RunError<RedisError>), +} + +pub async fn redis_conn( +) -> Result<PooledConnection<'static, RedisConnectionManager>, RedisConnError> { + if !CONN_POOL.initialized() { + let init_res = init_conn_pool().await; + + if let Err(err) = init_res { + return Err(RedisConnError::RedisErr(err)); + } } + + CONN_POOL + .get() + .unwrap() + .get() + .await + .map_err(RedisConnError::Bb8PoolErr) } /// prefix redis key @@ -49,23 +113,26 @@ pub fn key(key: impl ToString) -> String { mod unit_test { use super::redis_conn; use pretty_assertions::assert_eq; - use redis::Commands; + use redis::AsyncCommands; - #[test] - fn connect() { - assert!(redis_conn().is_ok()); - assert!(redis_conn().is_ok()); + #[tokio::test] + async fn connect() { + assert!(redis_conn().await.is_ok()); + assert!(redis_conn().await.is_ok()); } - #[test] - fn access() { - let mut redis = redis_conn().unwrap(); + #[tokio::test] + async fn access() { + let mut redis = redis_conn().await.unwrap(); let key = "CARGO_UNIT_TEST_KEY"; let value = "CARGO_UNIT_TEST_VALUE"; - assert_eq!(redis.set::<&str, &str, String>(key, value).unwrap(), "OK"); - assert_eq!(redis.get::<&str, String>(key).unwrap(), value); - assert_eq!(redis.del::<&str, u32>(key).unwrap(), 1); + assert_eq!( + redis.set::<&str, &str, String>(key, value).await.unwrap(), + "OK" + ); + assert_eq!(redis.get::<&str, String>(key).await.unwrap(), value); + assert_eq!(redis.del::<&str, u32>(key).await.unwrap(), 1); } } diff --git a/packages/backend-rs/src/init/greet.rs b/packages/backend-rs/src/init/greet.rs new file mode 100644 index 0000000000..6cc40d0013 --- /dev/null +++ b/packages/backend-rs/src/init/greet.rs @@ -0,0 +1,20 @@ +use crate::config::server::VERSION; + +const GREETING_MESSAGE: &str = "\ +███████╗██╗██████╗ ███████╗███████╗██╗███████╗██╗ ██╗ ○ ▄ ▄ +██╔════╝██║██╔══██╗██╔════╝██╔════╝██║██╔════╝██║ ██║ ⚬ █▄▄ █▄▄ +█████╗ ██║██████╔╝█████╗ █████╗ ██║███████╗███████║ ▄▄▄▄▄▄ ▄ +██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║╚════██║██╔══██║ █ █ █▄▄ +██║ ██║██║ ██║███████╗██║ ██║███████║██║ ██║ █ ● ● █ +╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ▀▄▄▄▄▄▄▀ + Firefish is an open-source decentralized microblogging platform. + If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish +"; + +#[crate::export] +pub fn greet() { + println!("{}", GREETING_MESSAGE); + + tracing::info!("Welcome to Firefish!"); + tracing::info!("Firefish {VERSION}"); +} diff --git a/packages/backend-rs/src/init/mod.rs b/packages/backend-rs/src/init/mod.rs index 6a72c210d1..e76c732184 100644 --- a/packages/backend-rs/src/init/mod.rs +++ b/packages/backend-rs/src/init/mod.rs @@ -1,2 +1,3 @@ -pub mod hardware_stats; +pub mod greet; pub mod log; +pub mod system_info; diff --git a/packages/backend-rs/src/init/hardware_stats.rs b/packages/backend-rs/src/init/system_info.rs similarity index 85% rename from packages/backend-rs/src/init/hardware_stats.rs rename to packages/backend-rs/src/init/system_info.rs index b57cb6e47b..4fb6e3a21f 100644 --- a/packages/backend-rs/src/init/hardware_stats.rs +++ b/packages/backend-rs/src/init/system_info.rs @@ -1,16 +1,16 @@ use std::sync::{Mutex, MutexGuard, OnceLock, PoisonError}; use sysinfo::System; -pub type SystemMutexError = PoisonError<MutexGuard<'static, System>>; +pub type SysinfoPoisonError = PoisonError<MutexGuard<'static, System>>; // TODO: handle this in a more proper way when we move the entry point to backend-rs -pub fn system() -> Result<MutexGuard<'static, System>, SystemMutexError> { +pub fn system() -> Result<MutexGuard<'static, System>, SysinfoPoisonError> { pub static SYSTEM: OnceLock<Mutex<System>> = OnceLock::new(); SYSTEM.get_or_init(|| Mutex::new(System::new_all())).lock() } #[crate::export] -pub fn show_server_info() -> Result<(), SystemMutexError> { +pub fn show_server_info() -> Result<(), SysinfoPoisonError> { let system_info = system()?; tracing::info!( diff --git a/packages/backend-rs/src/misc/add_note_to_antenna.rs b/packages/backend-rs/src/misc/add_note_to_antenna.rs deleted file mode 100644 index 2ce4e655d7..0000000000 --- a/packages/backend-rs/src/misc/add_note_to_antenna.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::database::{redis_conn, redis_key}; -use crate::model::entity::note; -use crate::service::stream; -use crate::util::id::{get_timestamp, InvalidIdErr}; -use redis::{streams::StreamMaxlen, Commands, RedisError}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Redis error: {0}")] - RedisErr(#[from] RedisError), - #[error("Invalid ID: {0}")] - InvalidIdErr(#[from] InvalidIdErr), - #[error("Stream error: {0}")] - StreamErr(#[from] stream::Error), -} - -type Note = note::Model; - -#[crate::export] -pub fn add_note_to_antenna(antenna_id: String, note: &Note) -> Result<(), Error> { - // for timeline API - redis_conn()?.xadd_maxlen( - redis_key(format!("antennaTimeline:{}", antenna_id)), - StreamMaxlen::Approx(200), - format!("{}-*", get_timestamp(¬e.id)?), - &[("note", ¬e.id)], - )?; - - // for streaming API - Ok(stream::antenna::publish(antenna_id, note)?) -} diff --git a/packages/backend-rs/src/misc/check_hit_antenna.rs b/packages/backend-rs/src/misc/check_hit_antenna.rs new file mode 100644 index 0000000000..2b70f0ae8c --- /dev/null +++ b/packages/backend-rs/src/misc/check_hit_antenna.rs @@ -0,0 +1,175 @@ +use crate::config::CONFIG; +use crate::database::{cache, db_conn}; +use crate::federation::acct::Acct; +use crate::misc::get_note_all_texts::{all_texts, NoteLike}; +use crate::model::entity::{antenna, blocking, following, note, sea_orm_active_enums::*}; +use sea_orm::{ColumnTrait, DbErr, EntityTrait, QueryFilter, QuerySelect}; + +#[derive(thiserror::Error, Debug)] +pub enum AntennaCheckError { + #[error("Database error: {0}")] + DbErr(#[from] DbErr), + #[error("Cache error: {0}")] + CacheErr(#[from] cache::Error), + #[error("User profile not found: {0}")] + UserProfileNotFoundErr(String), +} + +fn match_all(space_separated_words: &str, text: &str, case_sensitive: bool) -> bool { + if case_sensitive { + space_separated_words + .split_whitespace() + .all(|word| text.contains(word)) + } else { + space_separated_words + .to_lowercase() + .split_whitespace() + .all(|word| text.to_lowercase().contains(word)) + } +} + +pub async fn check_hit_antenna( + antenna: &antenna::Model, + note: note::Model, + note_author: &Acct, +) -> Result<bool, AntennaCheckError> { + if note.visibility == NoteVisibilityEnum::Specified { + return Ok(false); + } + + if antenna.with_file && note.file_ids.is_empty() { + return Ok(false); + } + + if !antenna.with_replies && note.reply_id.is_some() { + return Ok(false); + } + + if antenna.src == AntennaSrcEnum::Users { + let is_from_one_of_specified_authors = antenna + .users + .iter() + .map(|s| s.parse::<Acct>().unwrap()) + .any(|acct| acct.username == note_author.username && acct.host == note_author.host); + + if !is_from_one_of_specified_authors { + return Ok(false); + } + } else if antenna.src == AntennaSrcEnum::Instances { + let is_from_one_of_specified_servers = antenna.instances.iter().any(|host| { + host.to_ascii_lowercase() + == note_author + .host + .clone() + .unwrap_or(CONFIG.host.clone()) + .to_ascii_lowercase() + }); + + if !is_from_one_of_specified_servers { + return Ok(false); + } + } + + // "Home", "Group", "List" sources are currently disabled + + let note_texts = all_texts(NoteLike { + file_ids: note.file_ids, + user_id: note.user_id.clone(), + text: note.text, + cw: note.cw, + renote_id: note.renote_id, + reply_id: note.reply_id, + }) + .await?; + + let has_keyword = antenna.keywords.iter().any(|words| { + note_texts + .iter() + .any(|text| match_all(words, text, antenna.case_sensitive)) + }); + + if !has_keyword { + return Ok(false); + } + + let has_excluded_word = antenna.exclude_keywords.iter().any(|words| { + note_texts + .iter() + .any(|text| match_all(words, text, antenna.case_sensitive)) + }); + + if has_excluded_word { + return Ok(false); + } + + let db = db_conn().await?; + + let blocked_user_ids: Vec<String> = cache::get_one(cache::Category::Block, ¬e.user_id) + .await? + .unwrap_or({ + // cache miss + let blocks = blocking::Entity::find() + .select_only() + .column(blocking::Column::BlockeeId) + .filter(blocking::Column::BlockerId.eq(¬e.user_id)) + .into_tuple::<String>() + .all(db) + .await?; + cache::set_one(cache::Category::Block, ¬e.user_id, &blocks, 10 * 60).await?; + blocks + }); + + // if the antenna owner is blocked by the note author, return false + if blocked_user_ids.contains(&antenna.user_id) { + return Ok(false); + } + + if [NoteVisibilityEnum::Home, NoteVisibilityEnum::Followers].contains(¬e.visibility) { + let following_user_ids: Vec<String> = + cache::get_one(cache::Category::Follow, &antenna.user_id) + .await? + .unwrap_or({ + // cache miss + let following = following::Entity::find() + .select_only() + .column(following::Column::FolloweeId) + .filter(following::Column::FollowerId.eq(&antenna.user_id)) + .into_tuple::<String>() + .all(db) + .await?; + cache::set_one( + cache::Category::Follow, + &antenna.user_id, + &following, + 10 * 60, + ) + .await?; + following + }); + + // if the antenna owner is not following the note author, return false + if !following_user_ids.contains(¬e.user_id) { + return Ok(false); + } + } + + Ok(true) +} + +#[cfg(test)] +mod unit_test { + use super::match_all; + use pretty_assertions::assert_eq; + + #[test] + fn test_match_all() { + assert_eq!(match_all("Apple", "apple and banana", false), true); + assert_eq!(match_all("Apple", "apple and banana", true), false); + assert_eq!(match_all("Apple Banana", "apple and banana", false), true); + assert_eq!(match_all("Apple Banana", "apple and cinnamon", true), false); + assert_eq!( + match_all("Apple Banana", "apple and cinnamon", false), + false + ); + } +} diff --git a/packages/backend-rs/src/misc/check_word_mute.rs b/packages/backend-rs/src/misc/check_word_mute.rs index bfb8c72c58..7796daf798 100644 --- a/packages/backend-rs/src/misc/check_word_mute.rs +++ b/packages/backend-rs/src/misc/check_word_mute.rs @@ -1,84 +1,7 @@ -use crate::database::db_conn; -use crate::model::entity::{drive_file, note}; +use crate::misc::get_note_all_texts::{all_texts, NoteLike}; use once_cell::sync::Lazy; use regex::Regex; -use sea_orm::{prelude::*, QuerySelect}; - -// TODO: handle name collisions in a better way -#[crate::export(object, js_name = "NoteLikeForCheckWordMute")] -pub struct NoteLike { - pub file_ids: Vec<String>, - pub user_id: Option<String>, - pub text: Option<String>, - pub cw: Option<String>, - pub renote_id: Option<String>, - pub reply_id: Option<String>, -} - -async fn all_texts(note: NoteLike) -> Result<Vec<String>, DbErr> { - let db = db_conn().await?; - - let mut texts: Vec<String> = vec![]; - - if let Some(text) = note.text { - texts.push(text); - } - if let Some(cw) = note.cw { - texts.push(cw); - } - - texts.extend( - drive_file::Entity::find() - .select_only() - .column(drive_file::Column::Comment) - .filter(drive_file::Column::Id.is_in(note.file_ids)) - .into_tuple::<Option<String>>() - .all(db) - .await? - .into_iter() - .flatten(), - ); - - if let Some(renote_id) = ¬e.renote_id { - if let Some((text, cw)) = note::Entity::find_by_id(renote_id) - .select_only() - .columns([note::Column::Text, note::Column::Cw]) - .into_tuple::<(Option<String>, Option<String>)>() - .one(db) - .await? - { - if let Some(t) = text { - texts.push(t); - } - if let Some(c) = cw { - texts.push(c); - } - } else { - tracing::warn!("nonexistent renote id: {:#?}", renote_id); - } - } - - if let Some(reply_id) = ¬e.reply_id { - if let Some((text, cw)) = note::Entity::find_by_id(reply_id) - .select_only() - .columns([note::Column::Text, note::Column::Cw]) - .into_tuple::<(Option<String>, Option<String>)>() - .one(db) - .await? - { - if let Some(t) = text { - texts.push(t); - } - if let Some(c) = cw { - texts.push(c); - } - } else { - tracing::warn!("nonexistent reply id: {:#?}", reply_id); - } - } - - Ok(texts) -} +use sea_orm::DbErr; fn convert_regex(js_regex: &str) -> String { static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^/(.+)/(.*)$").unwrap()); @@ -87,14 +10,13 @@ fn convert_regex(js_regex: &str) -> String { fn check_word_mute_impl( texts: &[String], - muted_word_lists: &[Vec<String>], + muted_words: &[String], muted_patterns: &[String], ) -> bool { - muted_word_lists.iter().any(|muted_word_list| { + muted_words.iter().any(|item| { texts.iter().any(|text| { let text_lower = text.to_lowercase(); - muted_word_list - .iter() + item.split_whitespace() .all(|muted_word| text_lower.contains(&muted_word.to_lowercase())) }) }) || muted_patterns.iter().any(|muted_pattern| { @@ -107,16 +29,138 @@ fn check_word_mute_impl( #[crate::export] pub async fn check_word_mute( note: NoteLike, - muted_word_lists: Vec<Vec<String>>, - muted_patterns: Vec<String>, + muted_words: &[String], + muted_patterns: &[String], ) -> Result<bool, DbErr> { - if muted_word_lists.is_empty() && muted_patterns.is_empty() { + if muted_words.is_empty() && muted_patterns.is_empty() { Ok(false) } else { Ok(check_word_mute_impl( &all_texts(note).await?, - &muted_word_lists, - &muted_patterns, + muted_words, + muted_patterns, )) } } + +#[cfg(test)] +mod unit_test { + use super::check_word_mute_impl; + + #[test] + fn test_word_mute_match() { + let texts = vec![ + "The quick brown fox jumps over the lazy dog.".to_string(), + "色は匂へど 散りぬるを 我が世誰ぞ 常ならむ".to_string(), + "😇".to_string(), + ]; + + let hiragana_1 = r#"/[\u{3040}-\u{309f}]/u"#.to_string(); + let hiragana_2 = r#"/[あ-ん]/u"#.to_string(); + let katakana_1 = r#"/[\u{30a1}-\u{30ff}]/u"#.to_string(); + let katakana_2 = r#"/[ア-ン]/u"#.to_string(); + let emoji = r#"/[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}]/u"#.to_string(); + + assert!(check_word_mute_impl(&texts, &[], &["/the/i".to_string()])); + + assert!(!check_word_mute_impl(&texts, &[], &["/the/".to_string()])); + + assert!(check_word_mute_impl(&texts, &[], &["/QuICk/i".to_string()])); + + assert!(!check_word_mute_impl(&texts, &[], &["/QuICk/".to_string()])); + + assert!(check_word_mute_impl( + &texts, + &[ + "我".to_string(), + "有為の奥山 今日越えて 浅き夢見し 酔ひもせず".to_string() + ], + &[] + )); + + assert!(!check_word_mute_impl( + &texts, + &["有為の奥山 今日越えて 浅き夢見し 酔ひもせず".to_string()], + &[] + )); + + assert!(!check_word_mute_impl( + &texts, + &[ + "有為の奥山".to_string(), + "今日越えて".to_string(), + "浅き夢見し".to_string(), + "酔ひもせず".to_string() + ], + &[] + )); + + assert!(check_word_mute_impl( + &texts, + &["yellow fox".to_string(), "mastodon".to_string()], + &[hiragana_1.clone()] + )); + + assert!(check_word_mute_impl( + &texts, + &["yellow fox".to_string(), "mastodon".to_string()], + &[hiragana_2.clone()] + )); + + assert!(!check_word_mute_impl( + &texts, + &["yellow fox".to_string(), "mastodon".to_string()], + &[katakana_1.clone()] + )); + + assert!(!check_word_mute_impl( + &texts, + &["yellow fox".to_string(), "mastodon".to_string()], + &[katakana_2.clone()] + )); + + assert!(check_word_mute_impl( + &texts, + &["brown fox".to_string(), "mastodon".to_string()], + &[katakana_1.clone()] + )); + + assert!(check_word_mute_impl( + &texts, + &["brown fox".to_string(), "mastodon".to_string()], + &[katakana_2.clone()] + )); + + assert!(check_word_mute_impl( + &texts, + &["yellow fox".to_string(), "dog".to_string()], + &[katakana_1.clone()] + )); + + assert!(check_word_mute_impl( + &texts, + &["yellow fox".to_string(), "dog".to_string()], + &[katakana_2.clone()] + )); + + assert!(check_word_mute_impl( + &texts, + &["yellow fox".to_string(), "mastodon".to_string()], + &[hiragana_1.clone(), katakana_1.clone()] + )); + + assert!(check_word_mute_impl( + &texts, + &["😇".to_string(), "🥲".to_string(), "🥴".to_string()], + &[] + )); + + assert!(!check_word_mute_impl( + &texts, + &["🙂".to_string(), "🥲".to_string(), "🥴".to_string()], + &[] + )); + + assert!(check_word_mute_impl(&texts, &[], &[emoji.clone()])); + } +} diff --git a/packages/backend-rs/src/misc/get_image_size.rs b/packages/backend-rs/src/misc/get_image_size.rs index 60cf84aec1..750d87b6df 100644 --- a/packages/backend-rs/src/misc/get_image_size.rs +++ b/packages/backend-rs/src/misc/get_image_size.rs @@ -55,10 +55,12 @@ pub async fn get_image_size_from_url(url: &str) -> Result<ImageSize, Error> { { let _ = MTX_GUARD.lock().await; - attempted = cache::get_one::<bool>(cache::Category::FetchUrl, url)?.is_some(); + attempted = cache::get_one::<bool>(cache::Category::FetchUrl, url) + .await? + .is_some(); if !attempted { - cache::set_one(cache::Category::FetchUrl, url, &true, 10 * 60)?; + cache::set_one(cache::Category::FetchUrl, url, &true, 10 * 60).await?; } } @@ -138,7 +140,7 @@ mod unit_test { let mp3_url = "https://firefish.dev/firefish/firefish/-/blob/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/sounds/aisha/1.mp3"; // delete caches in case you run this test multiple times - cache::delete_all(cache::Category::FetchUrl).unwrap(); + cache::delete_all(cache::Category::FetchUrl).await.unwrap(); let png_size_1 = ImageSize { width: 1024, @@ -207,7 +209,9 @@ mod unit_test { let url = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/splash.png"; // delete caches in case you run this test multiple times - cache::delete_one(cache::Category::FetchUrl, url).unwrap(); + cache::delete_one(cache::Category::FetchUrl, url) + .await + .unwrap(); assert!(get_image_size_from_url(url).await.is_ok()); assert!(get_image_size_from_url(url).await.is_err()); diff --git a/packages/backend-rs/src/misc/get_note_all_texts.rs b/packages/backend-rs/src/misc/get_note_all_texts.rs new file mode 100644 index 0000000000..1e4bee396e --- /dev/null +++ b/packages/backend-rs/src/misc/get_note_all_texts.rs @@ -0,0 +1,79 @@ +use crate::database::db_conn; +use crate::model::entity::{drive_file, note}; +use sea_orm::{prelude::*, QuerySelect}; + +/// TODO: handle name collisions better +#[crate::export(object, js_name = "NoteLikeForAllTexts")] +pub struct NoteLike { + pub file_ids: Vec<String>, + pub user_id: String, + pub text: Option<String>, + pub cw: Option<String>, + pub renote_id: Option<String>, + pub reply_id: Option<String>, +} + +pub async fn all_texts(note: NoteLike) -> Result<Vec<String>, DbErr> { + let db = db_conn().await?; + + let mut texts: Vec<String> = vec![]; + + if let Some(text) = note.text { + texts.push(text); + } + if let Some(cw) = note.cw { + texts.push(cw); + } + + texts.extend( + drive_file::Entity::find() + .select_only() + .column(drive_file::Column::Comment) + .filter(drive_file::Column::Id.is_in(note.file_ids)) + .into_tuple::<Option<String>>() + .all(db) + .await? + .into_iter() + .flatten(), + ); + + if let Some(renote_id) = ¬e.renote_id { + if let Some((text, cw)) = note::Entity::find_by_id(renote_id) + .select_only() + .columns([note::Column::Text, note::Column::Cw]) + .into_tuple::<(Option<String>, Option<String>)>() + .one(db) + .await? + { + if let Some(t) = text { + texts.push(t); + } + if let Some(c) = cw { + texts.push(c); + } + } else { + tracing::warn!("nonexistent renote id: {:#?}", renote_id); + } + } + + if let Some(reply_id) = ¬e.reply_id { + if let Some((text, cw)) = note::Entity::find_by_id(reply_id) + .select_only() + .columns([note::Column::Text, note::Column::Cw]) + .into_tuple::<(Option<String>, Option<String>)>() + .one(db) + .await? + { + if let Some(t) = text { + texts.push(t); + } + if let Some(c) = cw { + texts.push(c); + } + } else { + tracing::warn!("nonexistent reply id: {:#?}", reply_id); + } + } + + Ok(texts) +} diff --git a/packages/backend-rs/src/misc/is_quote.rs b/packages/backend-rs/src/misc/is_quote.rs new file mode 100644 index 0000000000..5e8ce056c0 --- /dev/null +++ b/packages/backend-rs/src/misc/is_quote.rs @@ -0,0 +1,9 @@ +use crate::model::entity::note; + +// https://github.com/napi-rs/napi-rs/issues/2060 +type Note = note::Model; + +#[crate::export] +pub fn is_quote(note: Note) -> bool { + note.renote_id.is_some() && (note.text.is_some() || note.has_poll || !note.file_ids.is_empty()) +} diff --git a/packages/backend-rs/src/misc/latest_version.rs b/packages/backend-rs/src/misc/latest_version.rs index cea6901bbe..69a5a961ab 100644 --- a/packages/backend-rs/src/misc/latest_version.rs +++ b/packages/backend-rs/src/misc/latest_version.rs @@ -46,7 +46,7 @@ async fn get_latest_version() -> Result<String, Error> { #[crate::export] pub async fn latest_version() -> Result<String, Error> { let version: Option<String> = - cache::get_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL)?; + cache::get_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL).await?; if let Some(v) = version { tracing::trace!("use cached value: {}", v); @@ -61,7 +61,8 @@ pub async fn latest_version() -> Result<String, Error> { UPSTREAM_PACKAGE_JSON_URL, &fetched_version, 3 * 60 * 60, - )?; + ) + .await?; Ok(fetched_version) } } @@ -97,7 +98,9 @@ mod unit_test { #[tokio::test] async fn check_version() { // delete caches in case you run this test multiple times - cache::delete_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL).unwrap(); + cache::delete_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL) + .await + .unwrap(); // fetch from firefish.dev validate_version(latest_version().await.unwrap()); diff --git a/packages/backend-rs/src/misc/mod.rs b/packages/backend-rs/src/misc/mod.rs index ba1ad32c10..9255237dd0 100644 --- a/packages/backend-rs/src/misc/mod.rs +++ b/packages/backend-rs/src/misc/mod.rs @@ -1,4 +1,4 @@ -pub mod add_note_to_antenna; +pub mod check_hit_antenna; pub mod check_server_block; pub mod check_word_mute; pub mod convert_host; @@ -6,8 +6,9 @@ pub mod emoji; pub mod escape_sql; pub mod format_milliseconds; pub mod get_image_size; +pub mod get_note_all_texts; pub mod get_note_summary; -pub mod hardware_stats; +pub mod is_quote; pub mod is_safe_url; pub mod latest_version; pub mod mastodon_id; @@ -16,3 +17,4 @@ pub mod nyaify; pub mod password; pub mod reaction; pub mod remove_old_attestation_challenges; +pub mod system_info; diff --git a/packages/backend-rs/src/misc/hardware_stats.rs b/packages/backend-rs/src/misc/system_info.rs similarity index 90% rename from packages/backend-rs/src/misc/hardware_stats.rs rename to packages/backend-rs/src/misc/system_info.rs index f21e4c59a5..df0fa5bb9e 100644 --- a/packages/backend-rs/src/misc/hardware_stats.rs +++ b/packages/backend-rs/src/misc/system_info.rs @@ -1,4 +1,4 @@ -use crate::init::hardware_stats::{system, SystemMutexError}; +use crate::init::system_info::{system, SysinfoPoisonError}; use sysinfo::{Disks, MemoryRefreshKind}; // TODO: i64 -> u64 (we can't export u64 to Node.js) @@ -29,7 +29,7 @@ pub struct Storage { } #[crate::export] -pub fn cpu_info() -> Result<Cpu, SystemMutexError> { +pub fn cpu_info() -> Result<Cpu, SysinfoPoisonError> { let system_info = system()?; Ok(Cpu { @@ -45,7 +45,7 @@ pub fn cpu_info() -> Result<Cpu, SystemMutexError> { } #[crate::export] -pub fn cpu_usage() -> Result<f32, SystemMutexError> { +pub fn cpu_usage() -> Result<f32, SysinfoPoisonError> { let mut system_info = system()?; system_info.refresh_cpu_usage(); @@ -56,7 +56,7 @@ pub fn cpu_usage() -> Result<f32, SystemMutexError> { } #[crate::export] -pub fn memory_usage() -> Result<Memory, SystemMutexError> { +pub fn memory_usage() -> Result<Memory, SysinfoPoisonError> { let mut system_info = system()?; system_info.refresh_memory_specifics(MemoryRefreshKind::new().with_ram()); diff --git a/packages/backend-rs/src/model/entity/abuse_user_report.rs b/packages/backend-rs/src/model/entity/abuse_user_report.rs index e12b2f4d31..3846b0f2ec 100644 --- a/packages/backend-rs/src/model/entity/abuse_user_report.rs +++ b/packages/backend-rs/src/model/entity/abuse_user_report.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "targetUserId")] pub target_user_id: String, #[sea_orm(column_name = "reporterId")] diff --git a/packages/backend-rs/src/model/entity/access_token.rs b/packages/backend-rs/src/model/entity/access_token.rs index 8b79877b8b..beebdb2d2c 100644 --- a/packages/backend-rs/src/model/entity/access_token.rs +++ b/packages/backend-rs/src/model/entity/access_token.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, pub token: String, pub hash: String, #[sea_orm(column_name = "userId")] @@ -21,7 +21,7 @@ pub struct Model { #[sea_orm(column_name = "appId")] pub app_id: Option<String>, #[sea_orm(column_name = "lastUsedAt")] - pub last_used_at: Option<DateTime>, + pub last_used_at: Option<DateTimeWithTimeZone>, pub session: Option<String>, pub name: Option<String>, pub description: Option<String>, diff --git a/packages/backend-rs/src/model/entity/ad.rs b/packages/backend-rs/src/model/entity/ad.rs index 8a7b9abb86..ab8abcb9a8 100644 --- a/packages/backend-rs/src/model/entity/ad.rs +++ b/packages/backend-rs/src/model/entity/ad.rs @@ -13,9 +13,9 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "expiresAt")] - pub expires_at: DateTime, + pub expires_at: DateTimeWithTimeZone, pub place: String, pub priority: String, pub url: String, diff --git a/packages/backend-rs/src/model/entity/announcement.rs b/packages/backend-rs/src/model/entity/announcement.rs index 4532614a24..e6f3888fd5 100644 --- a/packages/backend-rs/src/model/entity/announcement.rs +++ b/packages/backend-rs/src/model/entity/announcement.rs @@ -13,13 +13,13 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, pub text: String, pub title: String, #[sea_orm(column_name = "imageUrl")] pub image_url: Option<String>, #[sea_orm(column_name = "updatedAt")] - pub updated_at: Option<DateTime>, + pub updated_at: Option<DateTimeWithTimeZone>, #[sea_orm(column_name = "showPopup")] pub show_popup: bool, #[sea_orm(column_name = "isGoodNews")] diff --git a/packages/backend-rs/src/model/entity/announcement_read.rs b/packages/backend-rs/src/model/entity/announcement_read.rs index de54dae082..6ab2d2cb52 100644 --- a/packages/backend-rs/src/model/entity/announcement_read.rs +++ b/packages/backend-rs/src/model/entity/announcement_read.rs @@ -17,7 +17,7 @@ pub struct Model { #[sea_orm(column_name = "announcementId")] pub announcement_id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/packages/backend-rs/src/model/entity/antenna.rs b/packages/backend-rs/src/model/entity/antenna.rs index 4074f5f106..9d746dbf5f 100644 --- a/packages/backend-rs/src/model/entity/antenna.rs +++ b/packages/backend-rs/src/model/entity/antenna.rs @@ -14,15 +14,13 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, pub name: String, pub src: AntennaSrcEnum, #[sea_orm(column_name = "userListId")] pub user_list_id: Option<String>, - #[sea_orm(column_type = "JsonBinary")] - pub keywords: Json, #[sea_orm(column_name = "withFile")] pub with_file: bool, pub expression: Option<String>, @@ -34,10 +32,10 @@ pub struct Model { #[sea_orm(column_name = "userGroupJoiningId")] pub user_group_joining_id: Option<String>, pub users: Vec<String>, - #[sea_orm(column_name = "excludeKeywords", column_type = "JsonBinary")] - pub exclude_keywords: Json, - #[sea_orm(column_type = "JsonBinary")] - pub instances: Json, + pub instances: Vec<String>, + pub keywords: Vec<String>, + #[sea_orm(column_name = "excludeKeywords")] + pub exclude_keywords: Vec<String>, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/packages/backend-rs/src/model/entity/app.rs b/packages/backend-rs/src/model/entity/app.rs index 8d2aa5373a..9d75e4fb4c 100644 --- a/packages/backend-rs/src/model/entity/app.rs +++ b/packages/backend-rs/src/model/entity/app.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: Option<String>, pub secret: String, diff --git a/packages/backend-rs/src/model/entity/attestation_challenge.rs b/packages/backend-rs/src/model/entity/attestation_challenge.rs index 999952943d..a64aabe619 100644 --- a/packages/backend-rs/src/model/entity/attestation_challenge.rs +++ b/packages/backend-rs/src/model/entity/attestation_challenge.rs @@ -16,7 +16,7 @@ pub struct Model { pub user_id: String, pub challenge: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "registrationChallenge")] pub registration_challenge: bool, } diff --git a/packages/backend-rs/src/model/entity/auth_session.rs b/packages/backend-rs/src/model/entity/auth_session.rs index fac397308d..30056374a3 100644 --- a/packages/backend-rs/src/model/entity/auth_session.rs +++ b/packages/backend-rs/src/model/entity/auth_session.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, pub token: String, #[sea_orm(column_name = "userId")] pub user_id: Option<String>, diff --git a/packages/backend-rs/src/model/entity/blocking.rs b/packages/backend-rs/src/model/entity/blocking.rs index d7773108ab..9bd192dc0a 100644 --- a/packages/backend-rs/src/model/entity/blocking.rs +++ b/packages/backend-rs/src/model/entity/blocking.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "blockeeId")] pub blockee_id: String, #[sea_orm(column_name = "blockerId")] diff --git a/packages/backend-rs/src/model/entity/channel.rs b/packages/backend-rs/src/model/entity/channel.rs index 4f3ec777ee..ab4574de57 100644 --- a/packages/backend-rs/src/model/entity/channel.rs +++ b/packages/backend-rs/src/model/entity/channel.rs @@ -13,9 +13,9 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "lastNotedAt")] - pub last_noted_at: Option<DateTime>, + pub last_noted_at: Option<DateTimeWithTimeZone>, #[sea_orm(column_name = "userId")] pub user_id: Option<String>, pub name: String, diff --git a/packages/backend-rs/src/model/entity/channel_following.rs b/packages/backend-rs/src/model/entity/channel_following.rs index d45b2c9706..29f0d46814 100644 --- a/packages/backend-rs/src/model/entity/channel_following.rs +++ b/packages/backend-rs/src/model/entity/channel_following.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "followeeId")] pub followee_id: String, #[sea_orm(column_name = "followerId")] diff --git a/packages/backend-rs/src/model/entity/channel_note_pining.rs b/packages/backend-rs/src/model/entity/channel_note_pining.rs index 9080c0181e..79f067d6cf 100644 --- a/packages/backend-rs/src/model/entity/channel_note_pining.rs +++ b/packages/backend-rs/src/model/entity/channel_note_pining.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "channelId")] pub channel_id: String, #[sea_orm(column_name = "noteId")] diff --git a/packages/backend-rs/src/model/entity/clip.rs b/packages/backend-rs/src/model/entity/clip.rs index 05dc66fa48..d1e1c18db2 100644 --- a/packages/backend-rs/src/model/entity/clip.rs +++ b/packages/backend-rs/src/model/entity/clip.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, pub name: String, diff --git a/packages/backend-rs/src/model/entity/drive_file.rs b/packages/backend-rs/src/model/entity/drive_file.rs index 699fa182a5..e44938dfc8 100644 --- a/packages/backend-rs/src/model/entity/drive_file.rs +++ b/packages/backend-rs/src/model/entity/drive_file.rs @@ -14,7 +14,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: Option<String>, #[sea_orm(column_name = "userHost")] diff --git a/packages/backend-rs/src/model/entity/drive_folder.rs b/packages/backend-rs/src/model/entity/drive_folder.rs index e4f6fccb6f..972a65e173 100644 --- a/packages/backend-rs/src/model/entity/drive_folder.rs +++ b/packages/backend-rs/src/model/entity/drive_folder.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, pub name: String, #[sea_orm(column_name = "userId")] pub user_id: Option<String>, diff --git a/packages/backend-rs/src/model/entity/emoji.rs b/packages/backend-rs/src/model/entity/emoji.rs index 374781c4d2..f1254dbcfe 100644 --- a/packages/backend-rs/src/model/entity/emoji.rs +++ b/packages/backend-rs/src/model/entity/emoji.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "updatedAt")] - pub updated_at: Option<DateTime>, + pub updated_at: Option<DateTimeWithTimeZone>, pub name: String, pub host: Option<String>, #[sea_orm(column_name = "originalUrl")] diff --git a/packages/backend-rs/src/model/entity/follow_request.rs b/packages/backend-rs/src/model/entity/follow_request.rs index 332eeed204..e4629aeaf5 100644 --- a/packages/backend-rs/src/model/entity/follow_request.rs +++ b/packages/backend-rs/src/model/entity/follow_request.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "followeeId")] pub followee_id: String, #[sea_orm(column_name = "followerId")] diff --git a/packages/backend-rs/src/model/entity/following.rs b/packages/backend-rs/src/model/entity/following.rs index 6f3255ecc9..386a37e12f 100644 --- a/packages/backend-rs/src/model/entity/following.rs +++ b/packages/backend-rs/src/model/entity/following.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "followeeId")] pub followee_id: String, #[sea_orm(column_name = "followerId")] diff --git a/packages/backend-rs/src/model/entity/gallery_like.rs b/packages/backend-rs/src/model/entity/gallery_like.rs index db519bc91b..faff8906aa 100644 --- a/packages/backend-rs/src/model/entity/gallery_like.rs +++ b/packages/backend-rs/src/model/entity/gallery_like.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "postId")] diff --git a/packages/backend-rs/src/model/entity/gallery_post.rs b/packages/backend-rs/src/model/entity/gallery_post.rs index 5492417911..e66a192ce6 100644 --- a/packages/backend-rs/src/model/entity/gallery_post.rs +++ b/packages/backend-rs/src/model/entity/gallery_post.rs @@ -13,9 +13,9 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "updatedAt")] - pub updated_at: DateTime, + pub updated_at: DateTimeWithTimeZone, pub title: String, pub description: Option<String>, #[sea_orm(column_name = "userId")] diff --git a/packages/backend-rs/src/model/entity/instance.rs b/packages/backend-rs/src/model/entity/instance.rs index 839c6206df..3bc0a6e7c7 100644 --- a/packages/backend-rs/src/model/entity/instance.rs +++ b/packages/backend-rs/src/model/entity/instance.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "caughtAt")] - pub caught_at: DateTime, + pub caught_at: DateTimeWithTimeZone, pub host: String, #[sea_orm(column_name = "usersCount")] pub users_count: i32, @@ -24,13 +24,13 @@ pub struct Model { #[sea_orm(column_name = "followersCount")] pub followers_count: i32, #[sea_orm(column_name = "latestRequestSentAt")] - pub latest_request_sent_at: Option<DateTime>, + pub latest_request_sent_at: Option<DateTimeWithTimeZone>, #[sea_orm(column_name = "latestStatus")] pub latest_status: Option<i32>, #[sea_orm(column_name = "latestRequestReceivedAt")] - pub latest_request_received_at: Option<DateTime>, + pub latest_request_received_at: Option<DateTimeWithTimeZone>, #[sea_orm(column_name = "lastCommunicatedAt")] - pub last_communicated_at: DateTime, + pub last_communicated_at: DateTimeWithTimeZone, #[sea_orm(column_name = "isNotResponding")] pub is_not_responding: bool, #[sea_orm(column_name = "softwareName")] @@ -46,7 +46,7 @@ pub struct Model { #[sea_orm(column_name = "maintainerEmail")] pub maintainer_email: Option<String>, #[sea_orm(column_name = "infoUpdatedAt")] - pub info_updated_at: Option<DateTime>, + pub info_updated_at: Option<DateTimeWithTimeZone>, #[sea_orm(column_name = "isSuspended")] pub is_suspended: bool, #[sea_orm(column_name = "iconUrl")] diff --git a/packages/backend-rs/src/model/entity/messaging_message.rs b/packages/backend-rs/src/model/entity/messaging_message.rs index b5f5d818a0..7665b58674 100644 --- a/packages/backend-rs/src/model/entity/messaging_message.rs +++ b/packages/backend-rs/src/model/entity/messaging_message.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "recipientId")] diff --git a/packages/backend-rs/src/model/entity/moderation_log.rs b/packages/backend-rs/src/model/entity/moderation_log.rs index e51ac4cc0d..5ede8bdaf4 100644 --- a/packages/backend-rs/src/model/entity/moderation_log.rs +++ b/packages/backend-rs/src/model/entity/moderation_log.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, pub r#type: String, diff --git a/packages/backend-rs/src/model/entity/muting.rs b/packages/backend-rs/src/model/entity/muting.rs index b9850b3a42..d323c5f4df 100644 --- a/packages/backend-rs/src/model/entity/muting.rs +++ b/packages/backend-rs/src/model/entity/muting.rs @@ -13,13 +13,13 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "muteeId")] pub mutee_id: String, #[sea_orm(column_name = "muterId")] pub muter_id: String, #[sea_orm(column_name = "expiresAt")] - pub expires_at: Option<DateTime>, + pub expires_at: Option<DateTimeWithTimeZone>, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/packages/backend-rs/src/model/entity/note.rs b/packages/backend-rs/src/model/entity/note.rs index 85947ea35d..55bda53353 100644 --- a/packages/backend-rs/src/model/entity/note.rs +++ b/packages/backend-rs/src/model/entity/note.rs @@ -14,7 +14,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "replyId")] pub reply_id: Option<String>, #[sea_orm(column_name = "renoteId")] @@ -66,7 +66,7 @@ pub struct Model { #[sea_orm(column_name = "threadId")] pub thread_id: Option<String>, #[sea_orm(column_name = "updatedAt")] - pub updated_at: Option<DateTime>, + pub updated_at: Option<DateTimeWithTimeZone>, pub lang: Option<String>, } diff --git a/packages/backend-rs/src/model/entity/note_edit.rs b/packages/backend-rs/src/model/entity/note_edit.rs index 0fb10b82a8..1bdb186cc5 100644 --- a/packages/backend-rs/src/model/entity/note_edit.rs +++ b/packages/backend-rs/src/model/entity/note_edit.rs @@ -20,7 +20,7 @@ pub struct Model { #[sea_orm(column_name = "fileIds")] pub file_ids: Vec<String>, #[sea_orm(column_name = "updatedAt")] - pub updated_at: DateTime, + pub updated_at: DateTimeWithTimeZone, pub emojis: Vec<String>, } diff --git a/packages/backend-rs/src/model/entity/note_favorite.rs b/packages/backend-rs/src/model/entity/note_favorite.rs index 12e5c641b1..0083803024 100644 --- a/packages/backend-rs/src/model/entity/note_favorite.rs +++ b/packages/backend-rs/src/model/entity/note_favorite.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "noteId")] diff --git a/packages/backend-rs/src/model/entity/note_reaction.rs b/packages/backend-rs/src/model/entity/note_reaction.rs index 9d5de3fab4..4d2e0df11a 100644 --- a/packages/backend-rs/src/model/entity/note_reaction.rs +++ b/packages/backend-rs/src/model/entity/note_reaction.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "noteId")] diff --git a/packages/backend-rs/src/model/entity/note_thread_muting.rs b/packages/backend-rs/src/model/entity/note_thread_muting.rs index fbbb30e948..a90e4dba64 100644 --- a/packages/backend-rs/src/model/entity/note_thread_muting.rs +++ b/packages/backend-rs/src/model/entity/note_thread_muting.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "threadId")] diff --git a/packages/backend-rs/src/model/entity/note_watching.rs b/packages/backend-rs/src/model/entity/note_watching.rs index 817431daaa..8e0c831cdd 100644 --- a/packages/backend-rs/src/model/entity/note_watching.rs +++ b/packages/backend-rs/src/model/entity/note_watching.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "noteId")] diff --git a/packages/backend-rs/src/model/entity/notification.rs b/packages/backend-rs/src/model/entity/notification.rs index e2273017e5..4877ca6f66 100644 --- a/packages/backend-rs/src/model/entity/notification.rs +++ b/packages/backend-rs/src/model/entity/notification.rs @@ -14,7 +14,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "notifieeId")] pub notifiee_id: String, #[sea_orm(column_name = "notifierId")] diff --git a/packages/backend-rs/src/model/entity/page.rs b/packages/backend-rs/src/model/entity/page.rs index a88b53459d..fef5d5c54b 100644 --- a/packages/backend-rs/src/model/entity/page.rs +++ b/packages/backend-rs/src/model/entity/page.rs @@ -14,9 +14,9 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "updatedAt")] - pub updated_at: DateTime, + pub updated_at: DateTimeWithTimeZone, pub title: String, pub name: String, pub summary: Option<String>, diff --git a/packages/backend-rs/src/model/entity/page_like.rs b/packages/backend-rs/src/model/entity/page_like.rs index 7f7caabde5..452f43fd26 100644 --- a/packages/backend-rs/src/model/entity/page_like.rs +++ b/packages/backend-rs/src/model/entity/page_like.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "pageId")] diff --git a/packages/backend-rs/src/model/entity/password_reset_request.rs b/packages/backend-rs/src/model/entity/password_reset_request.rs index de78019087..9a65693494 100644 --- a/packages/backend-rs/src/model/entity/password_reset_request.rs +++ b/packages/backend-rs/src/model/entity/password_reset_request.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, pub token: String, #[sea_orm(column_name = "userId")] pub user_id: String, diff --git a/packages/backend-rs/src/model/entity/poll.rs b/packages/backend-rs/src/model/entity/poll.rs index 2e65674b15..4cabf4cbaa 100644 --- a/packages/backend-rs/src/model/entity/poll.rs +++ b/packages/backend-rs/src/model/entity/poll.rs @@ -14,7 +14,7 @@ pub struct Model { #[sea_orm(column_name = "noteId", primary_key, auto_increment = false, unique)] pub note_id: String, #[sea_orm(column_name = "expiresAt")] - pub expires_at: Option<DateTime>, + pub expires_at: Option<DateTimeWithTimeZone>, pub multiple: bool, pub choices: Vec<String>, pub votes: Vec<i32>, diff --git a/packages/backend-rs/src/model/entity/poll_vote.rs b/packages/backend-rs/src/model/entity/poll_vote.rs index 47e68084cc..313618af16 100644 --- a/packages/backend-rs/src/model/entity/poll_vote.rs +++ b/packages/backend-rs/src/model/entity/poll_vote.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "noteId")] diff --git a/packages/backend-rs/src/model/entity/promo_note.rs b/packages/backend-rs/src/model/entity/promo_note.rs index 8b13b8987b..1c6e216991 100644 --- a/packages/backend-rs/src/model/entity/promo_note.rs +++ b/packages/backend-rs/src/model/entity/promo_note.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(column_name = "noteId", primary_key, auto_increment = false, unique)] pub note_id: String, #[sea_orm(column_name = "expiresAt")] - pub expires_at: DateTime, + pub expires_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, } diff --git a/packages/backend-rs/src/model/entity/promo_read.rs b/packages/backend-rs/src/model/entity/promo_read.rs index 3e6d822d29..17c2433cd3 100644 --- a/packages/backend-rs/src/model/entity/promo_read.rs +++ b/packages/backend-rs/src/model/entity/promo_read.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "noteId")] diff --git a/packages/backend-rs/src/model/entity/registration_ticket.rs b/packages/backend-rs/src/model/entity/registration_ticket.rs index ab735b2422..9192ac32a6 100644 --- a/packages/backend-rs/src/model/entity/registration_ticket.rs +++ b/packages/backend-rs/src/model/entity/registration_ticket.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, pub code: String, } diff --git a/packages/backend-rs/src/model/entity/registry_item.rs b/packages/backend-rs/src/model/entity/registry_item.rs index cb129128b7..66f3115b7c 100644 --- a/packages/backend-rs/src/model/entity/registry_item.rs +++ b/packages/backend-rs/src/model/entity/registry_item.rs @@ -13,9 +13,9 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "updatedAt")] - pub updated_at: DateTime, + pub updated_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, pub key: String, diff --git a/packages/backend-rs/src/model/entity/renote_muting.rs b/packages/backend-rs/src/model/entity/renote_muting.rs index 308e4c1563..6315a17212 100644 --- a/packages/backend-rs/src/model/entity/renote_muting.rs +++ b/packages/backend-rs/src/model/entity/renote_muting.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "muteeId")] pub mutee_id: String, #[sea_orm(column_name = "muterId")] diff --git a/packages/backend-rs/src/model/entity/reply_muting.rs b/packages/backend-rs/src/model/entity/reply_muting.rs index 77891d1ee2..5f24dcad52 100644 --- a/packages/backend-rs/src/model/entity/reply_muting.rs +++ b/packages/backend-rs/src/model/entity/reply_muting.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "muteeId")] pub mutee_id: String, #[sea_orm(column_name = "muterId")] diff --git a/packages/backend-rs/src/model/entity/signin.rs b/packages/backend-rs/src/model/entity/signin.rs index d5584f9d84..df5590a530 100644 --- a/packages/backend-rs/src/model/entity/signin.rs +++ b/packages/backend-rs/src/model/entity/signin.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, pub ip: String, diff --git a/packages/backend-rs/src/model/entity/sw_subscription.rs b/packages/backend-rs/src/model/entity/sw_subscription.rs index fe694177f3..bedb091701 100644 --- a/packages/backend-rs/src/model/entity/sw_subscription.rs +++ b/packages/backend-rs/src/model/entity/sw_subscription.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, pub endpoint: String, diff --git a/packages/backend-rs/src/model/entity/used_username.rs b/packages/backend-rs/src/model/entity/used_username.rs index 8557955034..c9bec5bd96 100644 --- a/packages/backend-rs/src/model/entity/used_username.rs +++ b/packages/backend-rs/src/model/entity/used_username.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub username: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/packages/backend-rs/src/model/entity/user.rs b/packages/backend-rs/src/model/entity/user.rs index 3ceac93fb5..6329cada0a 100644 --- a/packages/backend-rs/src/model/entity/user.rs +++ b/packages/backend-rs/src/model/entity/user.rs @@ -14,11 +14,11 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "updatedAt")] - pub updated_at: Option<DateTime>, + pub updated_at: Option<DateTimeWithTimeZone>, #[sea_orm(column_name = "lastFetchedAt")] - pub last_fetched_at: Option<DateTime>, + pub last_fetched_at: Option<DateTimeWithTimeZone>, pub username: String, #[sea_orm(column_name = "usernameLower")] pub username_lower: String, @@ -62,7 +62,7 @@ pub struct Model { #[sea_orm(column_name = "followersUri")] pub followers_uri: Option<String>, #[sea_orm(column_name = "lastActiveDate")] - pub last_active_date: Option<DateTime>, + pub last_active_date: Option<DateTimeWithTimeZone>, #[sea_orm(column_name = "hideOnlineStatus")] pub hide_online_status: bool, #[sea_orm(column_name = "isDeleted")] diff --git a/packages/backend-rs/src/model/entity/user_group.rs b/packages/backend-rs/src/model/entity/user_group.rs index 387c50d422..d57a730a48 100644 --- a/packages/backend-rs/src/model/entity/user_group.rs +++ b/packages/backend-rs/src/model/entity/user_group.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, pub name: String, #[sea_orm(column_name = "userId")] pub user_id: String, diff --git a/packages/backend-rs/src/model/entity/user_group_invitation.rs b/packages/backend-rs/src/model/entity/user_group_invitation.rs index 289acf89df..852638b0e4 100644 --- a/packages/backend-rs/src/model/entity/user_group_invitation.rs +++ b/packages/backend-rs/src/model/entity/user_group_invitation.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "userGroupId")] diff --git a/packages/backend-rs/src/model/entity/user_group_invite.rs b/packages/backend-rs/src/model/entity/user_group_invite.rs index 62fcf0c524..14f01486f6 100644 --- a/packages/backend-rs/src/model/entity/user_group_invite.rs +++ b/packages/backend-rs/src/model/entity/user_group_invite.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "userGroupId")] diff --git a/packages/backend-rs/src/model/entity/user_group_joining.rs b/packages/backend-rs/src/model/entity/user_group_joining.rs index b29bdd11eb..fabd7eccbd 100644 --- a/packages/backend-rs/src/model/entity/user_group_joining.rs +++ b/packages/backend-rs/src/model/entity/user_group_joining.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "userGroupId")] diff --git a/packages/backend-rs/src/model/entity/user_ip.rs b/packages/backend-rs/src/model/entity/user_ip.rs index 9847fb8d65..77a9af9987 100644 --- a/packages/backend-rs/src/model/entity/user_ip.rs +++ b/packages/backend-rs/src/model/entity/user_ip.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key)] pub id: i32, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, pub ip: String, diff --git a/packages/backend-rs/src/model/entity/user_list.rs b/packages/backend-rs/src/model/entity/user_list.rs index 30dc6db715..ce6f3f0095 100644 --- a/packages/backend-rs/src/model/entity/user_list.rs +++ b/packages/backend-rs/src/model/entity/user_list.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, pub name: String, diff --git a/packages/backend-rs/src/model/entity/user_list_joining.rs b/packages/backend-rs/src/model/entity/user_list_joining.rs index 972cc34bb6..ae1c2ad429 100644 --- a/packages/backend-rs/src/model/entity/user_list_joining.rs +++ b/packages/backend-rs/src/model/entity/user_list_joining.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "userListId")] diff --git a/packages/backend-rs/src/model/entity/user_note_pining.rs b/packages/backend-rs/src/model/entity/user_note_pining.rs index 26f05274fd..fcdc3d9d52 100644 --- a/packages/backend-rs/src/model/entity/user_note_pining.rs +++ b/packages/backend-rs/src/model/entity/user_note_pining.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, #[sea_orm(column_name = "noteId")] diff --git a/packages/backend-rs/src/model/entity/user_pending.rs b/packages/backend-rs/src/model/entity/user_pending.rs index 43a5c2f1e2..9b13a2f655 100644 --- a/packages/backend-rs/src/model/entity/user_pending.rs +++ b/packages/backend-rs/src/model/entity/user_pending.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, pub code: String, pub username: String, pub email: String, diff --git a/packages/backend-rs/src/model/entity/user_profile.rs b/packages/backend-rs/src/model/entity/user_profile.rs index d461b11292..73dc97a446 100644 --- a/packages/backend-rs/src/model/entity/user_profile.rs +++ b/packages/backend-rs/src/model/entity/user_profile.rs @@ -32,8 +32,6 @@ pub struct Model { #[sea_orm(column_name = "twoFactorEnabled")] pub two_factor_enabled: bool, pub password: Option<String>, - #[sea_orm(column_name = "clientData", column_type = "JsonBinary")] - pub client_data: Json, #[sea_orm(column_name = "autoAcceptFollowed")] pub auto_accept_followed: bool, #[sea_orm(column_name = "alwaysMarkNsfw")] @@ -48,14 +46,10 @@ pub struct Model { pub use_password_less_login: bool, #[sea_orm(column_name = "pinnedPageId", unique)] pub pinned_page_id: Option<String>, - #[sea_orm(column_type = "JsonBinary")] - pub room: Json, #[sea_orm(column_name = "injectFeaturedNote")] pub inject_featured_note: bool, #[sea_orm(column_name = "enableWordMute")] pub enable_word_mute: bool, - #[sea_orm(column_name = "mutedWords", column_type = "JsonBinary")] - pub muted_words: Json, #[sea_orm(column_name = "mutingNotificationTypes")] pub muting_notification_types: Vec<UserProfileMutingnotificationtypesEnum>, #[sea_orm(column_name = "noCrawle")] @@ -64,8 +58,6 @@ pub struct Model { pub receive_announcement_email: bool, #[sea_orm(column_name = "emailNotificationTypes", column_type = "JsonBinary")] pub email_notification_types: Json, - #[sea_orm(column_name = "mutedInstances", column_type = "JsonBinary")] - pub muted_instances: Json, #[sea_orm(column_name = "publicReactions")] pub public_reactions: bool, #[sea_orm(column_name = "ffVisibility")] @@ -78,6 +70,10 @@ pub struct Model { pub is_indexable: bool, #[sea_orm(column_name = "mutedPatterns")] pub muted_patterns: Vec<String>, + #[sea_orm(column_name = "mutedInstances")] + pub muted_instances: Vec<String>, + #[sea_orm(column_name = "mutedWords")] + pub muted_words: Vec<String>, pub lang: Option<String>, } diff --git a/packages/backend-rs/src/model/entity/user_security_key.rs b/packages/backend-rs/src/model/entity/user_security_key.rs index a4694cf177..0aba2916f0 100644 --- a/packages/backend-rs/src/model/entity/user_security_key.rs +++ b/packages/backend-rs/src/model/entity/user_security_key.rs @@ -17,7 +17,7 @@ pub struct Model { #[sea_orm(column_name = "publicKey")] pub public_key: String, #[sea_orm(column_name = "lastUsed")] - pub last_used: DateTime, + pub last_used: DateTimeWithTimeZone, pub name: String, } diff --git a/packages/backend-rs/src/model/entity/webhook.rs b/packages/backend-rs/src/model/entity/webhook.rs index 430cf9fcc0..5795550630 100644 --- a/packages/backend-rs/src/model/entity/webhook.rs +++ b/packages/backend-rs/src/model/entity/webhook.rs @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, #[sea_orm(column_name = "createdAt")] - pub created_at: DateTime, + pub created_at: DateTimeWithTimeZone, #[sea_orm(column_name = "userId")] pub user_id: String, pub name: String, @@ -22,7 +22,7 @@ pub struct Model { pub secret: String, pub active: bool, #[sea_orm(column_name = "latestSentAt")] - pub latest_sent_at: Option<DateTime>, + pub latest_sent_at: Option<DateTimeWithTimeZone>, #[sea_orm(column_name = "latestStatus")] pub latest_status: Option<i32>, } diff --git a/packages/backend-rs/src/service/antenna.rs b/packages/backend-rs/src/service/antenna.rs new file mode 100644 index 0000000000..3b7c8dba3a --- /dev/null +++ b/packages/backend-rs/src/service/antenna.rs @@ -0,0 +1,78 @@ +use crate::database::cache; +use crate::database::{db_conn, redis_conn, redis_key, RedisConnError}; +use crate::federation::acct::Acct; +use crate::misc::check_hit_antenna::{check_hit_antenna, AntennaCheckError}; +use crate::model::entity::{antenna, note}; +use crate::service::stream; +use crate::util::id::{get_timestamp, InvalidIdErr}; +use redis::{streams::StreamMaxlen, AsyncCommands, RedisError}; +use sea_orm::{DbErr, EntityTrait}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Database error: {0}")] + DbErr(#[from] DbErr), + #[error("Cache error: {0}")] + CacheErr(#[from] cache::Error), + #[error("Redis error: {0}")] + RedisErr(#[from] RedisError), + #[error("Redis connection error: {0}")] + RedisConnErr(#[from] RedisConnError), + #[error("Invalid ID: {0}")] + InvalidIdErr(#[from] InvalidIdErr), + #[error("Stream error: {0}")] + StreamErr(#[from] stream::Error), + #[error("Failed to check if the note should be added to antenna: {0}")] + AntennaCheckErr(#[from] AntennaCheckError), +} + +// https://github.com/napi-rs/napi-rs/issues/2060 +type Antenna = antenna::Model; +type Note = note::Model; + +// TODO?: it might be better to store this directly in memory +// (like fetch_meta) instead of Redis as it's used so much +async fn antennas() -> Result<Vec<Antenna>, Error> { + const CACHE_KEY: &str = "antennas"; + + Ok(cache::get::<Vec<Antenna>>(CACHE_KEY).await?.unwrap_or({ + let antennas = antenna::Entity::find().all(db_conn().await?).await?; + cache::set(CACHE_KEY, &antennas, 5 * 60).await?; + antennas + })) +} + +#[crate::export] +pub async fn update_antennas_on_new_note( + note: Note, + note_author: &Acct, + note_muted_users: Vec<String>, +) -> Result<(), Error> { + // TODO: do this in parallel + for antenna in antennas().await?.iter() { + if note_muted_users.contains(&antenna.user_id) { + continue; + } + if check_hit_antenna(antenna, note.clone(), note_author).await? { + add_note_to_antenna(&antenna.id, ¬e).await?; + } + } + + Ok(()) +} + +pub async fn add_note_to_antenna(antenna_id: &str, note: &Note) -> Result<(), Error> { + // for timeline API + redis_conn() + .await? + .xadd_maxlen( + redis_key(format!("antennaTimeline:{}", antenna_id)), + StreamMaxlen::Approx(200), + format!("{}-*", get_timestamp(¬e.id)?), + &[("note", ¬e.id)], + ) + .await?; + + // for streaming API + Ok(stream::antenna::publish(antenna_id.to_string(), note).await?) +} diff --git a/packages/backend-rs/src/service/mod.rs b/packages/backend-rs/src/service/mod.rs index 6de98f4674..4a5a0a7311 100644 --- a/packages/backend-rs/src/service/mod.rs +++ b/packages/backend-rs/src/service/mod.rs @@ -1,3 +1,4 @@ +pub mod antenna; pub mod nodeinfo; pub mod note; pub mod push_notification; diff --git a/packages/backend-rs/src/service/nodeinfo/generate.rs b/packages/backend-rs/src/service/nodeinfo/generate.rs index 3385c5a6f8..c88614ee63 100644 --- a/packages/backend-rs/src/service/nodeinfo/generate.rs +++ b/packages/backend-rs/src/service/nodeinfo/generate.rs @@ -116,13 +116,13 @@ async fn generate_nodeinfo_2_1() -> Result<Nodeinfo21, Error> { pub async fn nodeinfo_2_1() -> Result<Nodeinfo21, Error> { const NODEINFO_2_1_CACHE_KEY: &str = "nodeinfo_2_1"; - let cached = cache::get::<Nodeinfo21>(NODEINFO_2_1_CACHE_KEY)?; + let cached = cache::get::<Nodeinfo21>(NODEINFO_2_1_CACHE_KEY).await?; if let Some(nodeinfo) = cached { Ok(nodeinfo) } else { let nodeinfo = generate_nodeinfo_2_1().await?; - cache::set(NODEINFO_2_1_CACHE_KEY, &nodeinfo, 60 * 60)?; + cache::set(NODEINFO_2_1_CACHE_KEY, &nodeinfo, 60 * 60).await?; Ok(nodeinfo) } } diff --git a/packages/backend-rs/src/service/note/watch.rs b/packages/backend-rs/src/service/note/watch.rs index f740ec6ce4..69e30c0f9b 100644 --- a/packages/backend-rs/src/service/note/watch.rs +++ b/packages/backend-rs/src/service/note/watch.rs @@ -12,7 +12,7 @@ pub async fn watch_note( if watcher_id != note_author_id { note_watching::Entity::insert(note_watching::ActiveModel { id: ActiveValue::set(gen_id()), - created_at: ActiveValue::set(chrono::Local::now().naive_local()), + created_at: ActiveValue::set(chrono::Utc::now().into()), user_id: ActiveValue::Set(watcher_id.to_string()), note_user_id: ActiveValue::Set(note_author_id.to_string()), note_id: ActiveValue::Set(note_id.to_string()), diff --git a/packages/backend-rs/src/service/push_notification.rs b/packages/backend-rs/src/service/push_notification.rs index c33567138e..9552a76bc3 100644 --- a/packages/backend-rs/src/service/push_notification.rs +++ b/packages/backend-rs/src/service/push_notification.rs @@ -47,6 +47,7 @@ pub enum PushNotificationKind { ReadNotifications, #[strum(serialize = "readAllNotifications")] ReadAllNotifications, + Mastodon, } fn compact_content( @@ -158,15 +159,29 @@ pub async fn send_push_notification( .all(db) .await?; - let payload = format!( - "{{\"type\":\"{}\",\"userId\":\"{}\",\"dateTime\":{},\"body\":{}}}", - kind, - receiver_user_id, - chrono::Utc::now().timestamp_millis(), - serde_json::to_string(&compact_content(&kind, content.clone())?)? - ); + // TODO: refactoring + let payload = if kind == PushNotificationKind::Mastodon { + // Leave the `content` as it is + serde_json::to_string(content)? + } else { + // Format the `content` passed from the TypeScript backend + // for Firefish push notifications + format!( + "{{\"type\":\"{}\",\"userId\":\"{}\",\"dateTime\":{},\"body\":{}}}", + kind, + receiver_user_id, + chrono::Utc::now().timestamp_millis(), + serde_json::to_string(&compact_content(&kind, content.clone())?)? + ) + }; tracing::trace!("payload: {:#?}", payload); + let encoding = if kind == PushNotificationKind::Mastodon { + ContentEncoding::AesGcm + } else { + ContentEncoding::Aes128Gcm + }; + for subscription in subscriptions.iter() { if !subscription.send_read_message && [ @@ -211,7 +226,7 @@ pub async fn send_push_notification( let mut message_builder = WebPushMessageBuilder::new(&subscription_info); message_builder.set_ttl(1000); - message_builder.set_payload(ContentEncoding::Aes128Gcm, payload.as_bytes()); + message_builder.set_payload(encoding, payload.as_bytes()); message_builder.set_vapid_signature(signature.unwrap()); let message = message_builder.build(); diff --git a/packages/backend-rs/src/service/stream.rs b/packages/backend-rs/src/service/stream.rs index 279d343f10..699f45b691 100644 --- a/packages/backend-rs/src/service/stream.rs +++ b/packages/backend-rs/src/service/stream.rs @@ -7,8 +7,8 @@ pub mod group_chat; pub mod moderation; use crate::config::CONFIG; -use crate::database::redis_conn; -use redis::{Commands, RedisError}; +use crate::database::{redis_conn, RedisConnError}; +use redis::{AsyncCommands, RedisError}; #[derive(strum::Display)] pub enum Stream { @@ -49,13 +49,15 @@ pub enum Stream { pub enum Error { #[error("Redis error: {0}")] RedisError(#[from] RedisError), + #[error("Redis connection error: {0}")] + RedisConnErr(#[from] RedisConnError), #[error("Json (de)serialization error: {0}")] JsonError(#[from] serde_json::Error), #[error("Value error: {0}")] ValueError(String), } -pub fn publish_to_stream( +pub async fn publish_to_stream( stream: &Stream, kind: Option<String>, value: Option<String>, @@ -70,10 +72,13 @@ pub fn publish_to_stream( value.ok_or(Error::ValueError("Invalid streaming message".to_string()))? }; - redis_conn()?.publish( - &CONFIG.host, - format!("{{\"channel\":\"{}\",\"message\":{}}}", stream, message), - )?; + redis_conn() + .await? + .publish( + &CONFIG.host, + format!("{{\"channel\":\"{}\",\"message\":{}}}", stream, message), + ) + .await?; Ok(()) } diff --git a/packages/backend-rs/src/service/stream/antenna.rs b/packages/backend-rs/src/service/stream/antenna.rs index 3a829df546..3058d9f04c 100644 --- a/packages/backend-rs/src/service/stream/antenna.rs +++ b/packages/backend-rs/src/service/stream/antenna.rs @@ -1,10 +1,11 @@ use crate::model::entity::note; use crate::service::stream::{publish_to_stream, Error, Stream}; -pub fn publish(antenna_id: String, note: ¬e::Model) -> Result<(), Error> { +pub async fn publish(antenna_id: String, note: ¬e::Model) -> Result<(), Error> { publish_to_stream( &Stream::Antenna { antenna_id }, Some("note".to_string()), Some(serde_json::to_string(note)?), ) + .await } diff --git a/packages/backend-rs/src/service/stream/channel.rs b/packages/backend-rs/src/service/stream/channel.rs index 10a04c5e66..9f5cf3802a 100644 --- a/packages/backend-rs/src/service/stream/channel.rs +++ b/packages/backend-rs/src/service/stream/channel.rs @@ -1,10 +1,11 @@ use crate::service::stream::{publish_to_stream, Error, Stream}; #[crate::export(js_name = "publishToChannelStream")] -pub fn publish(channel_id: String, user_id: String) -> Result<(), Error> { +pub async fn publish(channel_id: String, user_id: String) -> Result<(), Error> { publish_to_stream( &Stream::Channel { channel_id }, Some("typing".to_string()), Some(format!("\"{}\"", user_id)), ) + .await } diff --git a/packages/backend-rs/src/service/stream/chat.rs b/packages/backend-rs/src/service/stream/chat.rs index 3015d921e1..84280c319c 100644 --- a/packages/backend-rs/src/service/stream/chat.rs +++ b/packages/backend-rs/src/service/stream/chat.rs @@ -17,7 +17,7 @@ pub enum ChatEvent { // https://github.com/napi-rs/napi-rs/issues/2036 #[crate::export(js_name = "publishToChatStream")] -pub fn publish( +pub async fn publish( sender_user_id: String, receiver_user_id: String, kind: ChatEvent, @@ -31,4 +31,5 @@ pub fn publish( Some(kind.to_string()), Some(serde_json::to_string(object)?), ) + .await } diff --git a/packages/backend-rs/src/service/stream/chat_index.rs b/packages/backend-rs/src/service/stream/chat_index.rs index eb64384dca..6619c5589c 100644 --- a/packages/backend-rs/src/service/stream/chat_index.rs +++ b/packages/backend-rs/src/service/stream/chat_index.rs @@ -13,7 +13,7 @@ pub enum ChatIndexEvent { // https://github.com/napi-rs/napi-rs/issues/2036 #[crate::export(js_name = "publishToChatIndexStream")] -pub fn publish( +pub async fn publish( user_id: String, kind: ChatIndexEvent, object: &serde_json::Value, @@ -23,4 +23,5 @@ pub fn publish( Some(kind.to_string()), Some(serde_json::to_string(object)?), ) + .await } diff --git a/packages/backend-rs/src/service/stream/custom_emoji.rs b/packages/backend-rs/src/service/stream/custom_emoji.rs index 21158fc761..2cd67f5169 100644 --- a/packages/backend-rs/src/service/stream/custom_emoji.rs +++ b/packages/backend-rs/src/service/stream/custom_emoji.rs @@ -18,10 +18,11 @@ pub struct PackedEmoji { } #[crate::export(js_name = "publishToBroadcastStream")] -pub fn publish(emoji: &PackedEmoji) -> Result<(), Error> { +pub async fn publish(emoji: &PackedEmoji) -> Result<(), Error> { publish_to_stream( &Stream::CustomEmoji, Some("emojiAdded".to_string()), Some(format!("{{\"emoji\":{}}}", serde_json::to_string(emoji)?)), ) + .await } diff --git a/packages/backend-rs/src/service/stream/group_chat.rs b/packages/backend-rs/src/service/stream/group_chat.rs index 1e676bbef5..20c04c6fa2 100644 --- a/packages/backend-rs/src/service/stream/group_chat.rs +++ b/packages/backend-rs/src/service/stream/group_chat.rs @@ -4,10 +4,15 @@ use crate::service::stream::{chat::ChatEvent, publish_to_stream, Error, Stream}; // https://github.com/napi-rs/napi-rs/issues/2036 #[crate::export(js_name = "publishToGroupChatStream")] -pub fn publish(group_id: String, kind: ChatEvent, object: &serde_json::Value) -> Result<(), Error> { +pub async fn publish( + group_id: String, + kind: ChatEvent, + object: &serde_json::Value, +) -> Result<(), Error> { publish_to_stream( &Stream::GroupChat { group_id }, Some(kind.to_string()), Some(serde_json::to_string(object)?), ) + .await } diff --git a/packages/backend-rs/src/service/stream/moderation.rs b/packages/backend-rs/src/service/stream/moderation.rs index 576bf9fd21..ef604ed6bf 100644 --- a/packages/backend-rs/src/service/stream/moderation.rs +++ b/packages/backend-rs/src/service/stream/moderation.rs @@ -12,10 +12,11 @@ pub struct AbuseUserReportLike { } #[crate::export(js_name = "publishToModerationStream")] -pub fn publish(moderator_id: String, report: &AbuseUserReportLike) -> Result<(), Error> { +pub async fn publish(moderator_id: String, report: &AbuseUserReportLike) -> Result<(), Error> { publish_to_stream( &Stream::Moderation { moderator_id }, Some("newAbuseUserReport".to_string()), Some(serde_json::to_string(report)?), ) + .await } diff --git a/packages/backend/package.json b/packages/backend/package.json index ddc28deca1..c05ef91726 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -36,15 +36,14 @@ "adm-zip": "0.5.10", "ajv": "8.13.0", "archiver": "7.0.1", - "aws-sdk": "2.1621.0", + "aws-sdk": "2.1623.0", "axios": "1.6.8", "backend-rs": "workspace:*", "blurhash": "2.0.5", - "bull": "4.12.4", + "bull": "4.12.6", "cacheable-lookup": "TheEssem/cacheable-lookup", "cbor-x": "1.5.9", "chalk": "5.3.0", - "chalk-template": "1.1.0", "cli-highlight": "2.1.11", "color-convert": "2.0.1", "content-disposition": "0.5.4", @@ -58,7 +57,7 @@ "firefish-js": "workspace:*", "fluent-ffmpeg": "2.1.2", "form-data": "4.0.0", - "got": "14.2.1", + "got": "14.3.0", "gunzip-maybe": "1.4.2", "hpagent": "1.2.0", "ioredis": "5.4.1", @@ -107,7 +106,7 @@ "rss-parser": "3.13.0", "sanitize-html": "2.13.0", "semver": "7.6.2", - "sharp": "0.33.3", + "sharp": "0.33.4", "stringz": "2.1.0", "summaly": "2.7.0", "syslog-pro": "1.0.0", @@ -169,7 +168,7 @@ "@types/websocket": "1.0.10", "@types/ws": "8.5.10", "cross-env": "7.0.3", - "eslint": "9.2.0", + "eslint": "9.3.0", "mocha": "10.4.0", "pug": "3.0.2", "strict-event-emitter-types": "2.0.0", diff --git a/packages/backend/src/@types/backend-rs.d.ts b/packages/backend/src/@types/backend-rs.d.ts new file mode 100644 index 0000000000..c1a6cc71be --- /dev/null +++ b/packages/backend/src/@types/backend-rs.d.ts @@ -0,0 +1 @@ +type DateTimeWithTimeZone = Date; diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index 68e7863628..b331eb898d 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -1,15 +1,11 @@ -import * as fs from "node:fs"; -import { fileURLToPath } from "node:url"; -import { dirname } from "node:path"; import * as os from "node:os"; import cluster from "node:cluster"; -import chalk from "chalk"; -import chalkTemplate from "chalk-template"; import semver from "semver"; import Logger from "@/services/logger.js"; import { fetchMeta, + greet, initializeRustLogger, removeOldAttestationChallenges, showServerInfo, @@ -19,74 +15,9 @@ import { config, envOption } from "@/config.js"; import { db, initDb } from "@/db/postgre.js"; import { inspect } from "node:util"; -const _filename = fileURLToPath(import.meta.url); -const _dirname = dirname(_filename); - -const meta = JSON.parse( - fs.readFileSync(`${_dirname}/../../../../built/meta.json`, "utf-8"), -); - const logger = new Logger("core", "cyan"); const bootLogger = logger.createSubLogger("boot", "magenta", false); -const themeColor = chalk.hex("#31748f"); - -function greet() { - //#region Firefish logo - console.log( - themeColor( - "██████╗ ██╗██████╗ ███████╗███████╗██╗███████╗██╗ ██╗ ○ ▄ ▄ ", - ), - ); - console.log( - themeColor( - "██╔════╝██║██╔══██╗██╔════╝██╔════╝██║██╔════╝██║ ██║ ⚬ █▄▄ █▄▄ ", - ), - ); - console.log( - themeColor( - "█████╗ ██║██████╔╝█████╗ █████╗ ██║███████╗███████║ ▄▄▄▄▄▄ ▄ ", - ), - ); - console.log( - themeColor( - "██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║╚════██║██╔══██║ █ █ █▄▄ ", - ), - ); - console.log( - themeColor( - "██║ ██║██║ ██║███████╗██║ ██║███████║██║ ██║ █ ● ● █ ", - ), - ); - console.log( - themeColor( - "╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ▀▄▄▄▄▄▄▀ ", - ), - ); - //#endregion - - console.log( - " Firefish is an open-source decentralized microblogging platform.", - ); - console.log( - chalk.rgb( - 255, - 136, - 0, - )( - " If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish", - ), - ); - - console.log(""); - console.log( - chalkTemplate`--- ${os.hostname()} {gray (PID: ${process.pid.toString()})} ---`, - ); - - bootLogger.info("Welcome to Firefish!"); - bootLogger.info(`Firefish v${meta.version}`, null, true); -} - /** * Init master process */ diff --git a/packages/backend/src/migration/1714192520471-antenna-jsonb-to-array.ts b/packages/backend/src/migration/1714192520471-antenna-jsonb-to-array.ts new file mode 100644 index 0000000000..526e62d438 --- /dev/null +++ b/packages/backend/src/migration/1714192520471-antenna-jsonb-to-array.ts @@ -0,0 +1,118 @@ +import type { MigrationInterface, QueryRunner } from "typeorm"; + +export class AntennaJsonbToArray1714192520471 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query( + `ALTER TABLE "antenna" RENAME COLUMN "instances" TO "instances_old"`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD COLUMN "instances" character varying(512)[] NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `UPDATE "antenna" SET "instances" = ARRAY(SELECT jsonb_array_elements_text("instances_old"))::character varying(512)[]`, + ); + await queryRunner.query( + `UPDATE "antenna" SET "instances" = '{}' WHERE "instances" = '{""}'`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" DROP COLUMN "instances_old"`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" RENAME COLUMN "keywords" TO "keywords_old"`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD COLUMN "keywords" text[] NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TEMP TABLE "HMyeXPcdtQYGsSrf" ("id" character varying(32), "kws" text[])`, + ); + await queryRunner.query( + `INSERT INTO "HMyeXPcdtQYGsSrf" ("id", "kws") SELECT "id", array_agg("X"."w") FROM (SELECT "id", array_to_string(ARRAY(SELECT jsonb_array_elements_text("kw")), ' ') AS "w" FROM (SELECT "id", jsonb_array_elements("keywords_old") AS "kw" FROM "antenna") AS "a") AS "X" GROUP BY "id"`, + ); + await queryRunner.query( + `UPDATE "antenna" SET "keywords" = "kws" FROM "HMyeXPcdtQYGsSrf" WHERE "antenna"."id" = "HMyeXPcdtQYGsSrf"."id"`, + ); + await queryRunner.query( + `UPDATE "antenna" SET "keywords" = '{}' WHERE "keywords" = '{""}'`, + ); + await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "keywords_old"`); + await queryRunner.query( + `ALTER TABLE "antenna" RENAME COLUMN "excludeKeywords" TO "excludeKeywords_old"`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD COLUMN "excludeKeywords" text[] NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TEMP TABLE "kpdsACdZTRYqLkfK" ("id" character varying(32), "kws" text[])`, + ); + await queryRunner.query( + `INSERT INTO "kpdsACdZTRYqLkfK" ("id", "kws") SELECT "id", array_agg("X"."w") FROM (SELECT "id", array_to_string(ARRAY(SELECT jsonb_array_elements_text("kw")), ' ') AS "w" FROM (SELECT "id", jsonb_array_elements("excludeKeywords_old") AS "kw" FROM "antenna") AS "a") AS "X" GROUP BY "id"`, + ); + await queryRunner.query( + `UPDATE "antenna" SET "excludeKeywords" = "kws" FROM "kpdsACdZTRYqLkfK" WHERE "antenna"."id" = "kpdsACdZTRYqLkfK"."id"`, + ); + await queryRunner.query( + `UPDATE "antenna" SET "excludeKeywords" = '{}' WHERE "excludeKeywords" = '{""}'`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" DROP COLUMN "excludeKeywords_old"`, + ); + } + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query( + `UPDATE "antenna" SET "instances" = '{""}' WHERE "instances" = '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" RENAME COLUMN "instances" TO "instances_old"`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD COLUMN "instances" jsonb NOT NULL DEFAULT '[]'`, + ); + await queryRunner.query( + `UPDATE "antenna" SET "instances" = to_jsonb("instances_old")`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" DROP COLUMN "instances_old"`, + ); + await queryRunner.query( + `UPDATE "antenna" SET "keywords" = '{""}' WHERE "keywords" = '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" RENAME COLUMN "keywords" TO "keywords_old"`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD COLUMN "keywords" jsonb NOT NULL DEFAULT '[]'`, + ); + await queryRunner.query( + `CREATE TEMP TABLE "QvPNcMitBFkqqBgm" ("id" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]')`, + ); + await queryRunner.query( + `INSERT INTO "QvPNcMitBFkqqBgm" ("id", "kws") SELECT "id", jsonb_agg("X"."w") FROM (SELECT "id", to_jsonb(string_to_array(unnest("keywords_old"), ' ')) AS "w" FROM "antenna") AS "X" GROUP BY "id"`, + ); + await queryRunner.query( + `UPDATE "antenna" SET "keywords" = "kws" FROM "QvPNcMitBFkqqBgm" WHERE "antenna"."id" = "QvPNcMitBFkqqBgm"."id"`, + ); + await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "keywords_old"`); + await queryRunner.query( + `UPDATE "antenna" SET "excludeKeywords" = '{""}' WHERE "excludeKeywords" = '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" RENAME COLUMN "excludeKeywords" TO "excludeKeywords_old"`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD COLUMN "excludeKeywords" jsonb NOT NULL DEFAULT '[]'`, + ); + await queryRunner.query( + `CREATE TEMP TABLE "MZvVSjHzYcGXmGmz" ("id" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]')`, + ); + await queryRunner.query( + `INSERT INTO "MZvVSjHzYcGXmGmz" ("id", "kws") SELECT "id", jsonb_agg("X"."w") FROM (SELECT "id", to_jsonb(string_to_array(unnest("excludeKeywords_old"), ' ')) AS "w" FROM "antenna") AS "X" GROUP BY "id"`, + ); + await queryRunner.query( + `UPDATE "antenna" SET "excludeKeywords" = "kws" FROM "MZvVSjHzYcGXmGmz" WHERE "antenna"."id" = "MZvVSjHzYcGXmGmz"."id"`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" DROP COLUMN "excludeKeywords_old"`, + ); + } +} diff --git a/packages/backend/src/migration/1714259023878-drop-unused-userprofile-columns.ts b/packages/backend/src/migration/1714259023878-drop-unused-userprofile-columns.ts new file mode 100644 index 0000000000..71c236972d --- /dev/null +++ b/packages/backend/src/migration/1714259023878-drop-unused-userprofile-columns.ts @@ -0,0 +1,27 @@ +import type { MigrationInterface, QueryRunner } from "typeorm"; + +export class DropUnusedUserprofileColumns1714259023878 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "clientData"`, + ); + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "room"`); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "room" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."room" IS 'The room data of the User.'`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "clientData" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."clientData" IS 'The client-specific data of the User.'`, + ); + } +} diff --git a/packages/backend/src/migration/1714270605574-userprofile-jsonb-to-array.ts b/packages/backend/src/migration/1714270605574-userprofile-jsonb-to-array.ts new file mode 100644 index 0000000000..02ef6cc5fa --- /dev/null +++ b/packages/backend/src/migration/1714270605574-userprofile-jsonb-to-array.ts @@ -0,0 +1,71 @@ +import type { MigrationInterface, QueryRunner } from "typeorm"; + +export class UserprofileJsonbToArray1714270605574 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query( + `ALTER TABLE "user_profile" RENAME COLUMN "mutedInstances" TO "mutedInstances_old"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD COLUMN "mutedInstances" character varying(512)[] NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `UPDATE "user_profile" SET "mutedInstances" = ARRAY(SELECT jsonb_array_elements_text("mutedInstances_old"))::character varying(512)[]`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "mutedInstances_old"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" RENAME COLUMN "mutedWords" TO "mutedWords_old"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD COLUMN "mutedWords" text[] NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TEMP TABLE "MmVqAUUgpshTCQcw" ("userId" character varying(32), "kws" text[])`, + ); + await queryRunner.query( + `INSERT INTO "MmVqAUUgpshTCQcw" ("userId", "kws") SELECT "userId", array_agg("X"."w") FROM (SELECT "userId", array_to_string(ARRAY(SELECT jsonb_array_elements_text("kw")), ' ') AS "w" FROM (SELECT "userId", jsonb_array_elements("mutedWords_old") AS "kw" FROM "user_profile") AS "a") AS "X" GROUP BY "userId"`, + ); + await queryRunner.query( + `UPDATE "user_profile" SET "mutedWords" = "kws" FROM "MmVqAUUgpshTCQcw" WHERE "user_profile"."userId" = "MmVqAUUgpshTCQcw"."userId"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "mutedWords_old"`, + ); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query( + `ALTER TABLE "user_profile" RENAME COLUMN "mutedInstances" TO "mutedInstances_old"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD COLUMN "mutedInstances" jsonb NOT NULL DEFAULT '[]'`, + ); + await queryRunner.query( + `UPDATE "user_profile" SET "mutedInstances" = to_jsonb("mutedInstances_old")`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "mutedInstances_old"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" RENAME COLUMN "mutedWords" TO "mutedWords_old"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD COLUMN "mutedWords" jsonb NOT NULL DEFAULT '[]'`, + ); + await queryRunner.query( + `CREATE TEMP TABLE "BCrsGgLCUeMMLARy" ("userId" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]')`, + ); + await queryRunner.query( + `INSERT INTO "BCrsGgLCUeMMLARy" ("userId", "kws") SELECT "userId", jsonb_agg("X"."w") FROM (SELECT "userId", to_jsonb(string_to_array(unnest("mutedWords_old"), ' ')) AS "w" FROM "user_profile") AS "X" GROUP BY "userId"`, + ); + await queryRunner.query( + `UPDATE "user_profile" SET "mutedWords" = "kws" FROM "BCrsGgLCUeMMLARy" WHERE "user_profile"."userId" = "BCrsGgLCUeMMLARy"."userId"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "mutedWords_old"`, + ); + } +} diff --git a/packages/backend/src/migration/1715351290096-add-back-timezone.ts b/packages/backend/src/migration/1715351290096-add-back-timezone.ts new file mode 100644 index 0000000000..51b6f2d842 --- /dev/null +++ b/packages/backend/src/migration/1715351290096-add-back-timezone.ts @@ -0,0 +1,459 @@ +import type { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddBackTimezone1715351290096 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ALTER "lastUsedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "ad" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "ad" ALTER "expiresAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "announcement" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "announcement" ALTER "updatedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "announcement_read" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "app" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "attestation_challenge" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "auth_session" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "blocking" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "channel" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "channel" ALTER "lastNotedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "channel_following" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "channel_note_pining" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "clip" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "drive_folder" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "emoji" ALTER "updatedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "following" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "follow_request" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "gallery_like" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "gallery_post" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "gallery_post" ALTER "updatedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "instance" ALTER "caughtAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "instance" ALTER "infoUpdatedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "instance" ALTER "lastCommunicatedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "instance" ALTER "latestRequestReceivedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "instance" ALTER "latestRequestSentAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "moderation_log" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "muting" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "muting" ALTER "expiresAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note" ALTER "updatedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note_edit" ALTER "updatedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note_favorite" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note_reaction" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note_thread_muting" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note_watching" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "notification" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "page" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "page" ALTER "updatedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "page_like" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "password_reset_request" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "poll" ALTER "expiresAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "poll_vote" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "promo_note" ALTER "expiresAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "promo_read" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "registration_ticket" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "registry_item" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "registry_item" ALTER "updatedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "renote_muting" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "reply_muting" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "signin" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "sw_subscription" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "used_username" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user" ALTER "lastActiveDate" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user" ALTER "lastFetchedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user" ALTER "updatedAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_group" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_group_invitation" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_group_invite" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_group_joining" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_ip" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_list" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_list_joining" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_note_pining" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_pending" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_security_key" ALTER "lastUsed" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "webhook" ALTER "createdAt" TYPE timestamp with time zone`, + ); + await queryRunner.query( + `ALTER TABLE "webhook" ALTER "latestSentAt" TYPE timestamp with time zone`, + ); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ALTER "lastUsedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "ad" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "ad" ALTER "expiresAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "announcement" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "announcement" ALTER "updatedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "announcement_read" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "app" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "attestation_challenge" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "auth_session" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "blocking" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "channel" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "channel" ALTER "lastNotedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "channel_following" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "channel_note_pining" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "clip" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "drive_folder" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "emoji" ALTER "updatedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "following" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "follow_request" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "gallery_like" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "gallery_post" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "gallery_post" ALTER "updatedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "instance" ALTER "caughtAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "instance" ALTER "infoUpdatedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "instance" ALTER "lastCommunicatedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "instance" ALTER "latestRequestReceivedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "instance" ALTER "latestRequestSentAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "moderation_log" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "muting" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "muting" ALTER "expiresAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note" ALTER "updatedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note_edit" ALTER "updatedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note_favorite" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note_reaction" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note_thread_muting" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "note_watching" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "notification" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "page" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "page" ALTER "updatedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "page_like" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "password_reset_request" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "poll" ALTER "expiresAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "poll_vote" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "promo_note" ALTER "expiresAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "promo_read" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "registration_ticket" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "registry_item" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "registry_item" ALTER "updatedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "renote_muting" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "reply_muting" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "signin" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "sw_subscription" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "used_username" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user" ALTER "lastActiveDate" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user" ALTER "lastFetchedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user" ALTER "updatedAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_group" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_group_invitation" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_group_invite" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_group_joining" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_ip" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_list" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_list_joining" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_note_pining" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_pending" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "user_security_key" ALTER "lastUsed" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "webhook" ALTER "createdAt" TYPE timestamp without time zone`, + ); + await queryRunner.query( + `ALTER TABLE "webhook" ALTER "latestSentAt" TYPE timestamp without time zone`, + ); + } +} diff --git a/packages/backend/src/misc/antenna-cache.ts b/packages/backend/src/misc/antenna-cache.ts deleted file mode 100644 index 7f199c3967..0000000000 --- a/packages/backend/src/misc/antenna-cache.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Antennas } from "@/models/index.js"; -import type { Antenna } from "@/models/entities/antenna.js"; -import { subscriber } from "@/db/redis.js"; - -let antennasFetched = false; -let antennas: Antenna[] = []; - -export async function getAntennas() { - if (!antennasFetched) { - antennas = await Antennas.find(); - antennasFetched = true; - } - - return antennas; -} - -subscriber.on("message", async (_, data) => { - const obj = JSON.parse(data); - - if (obj.channel === "internal") { - const { type, body } = obj.message; - switch (type) { - case "antennaCreated": - antennas.push(body); - break; - case "antennaUpdated": - antennas[antennas.findIndex((a) => a.id === body.id)] = body; - break; - case "antennaDeleted": - antennas = antennas.filter((a) => a.id !== body.id); - break; - default: - break; - } - } -}); diff --git a/packages/backend/src/misc/check-hit-antenna.ts b/packages/backend/src/misc/check-hit-antenna.ts deleted file mode 100644 index 2fa764c85b..0000000000 --- a/packages/backend/src/misc/check-hit-antenna.ts +++ /dev/null @@ -1,132 +0,0 @@ -import type { Antenna } from "@/models/entities/antenna.js"; -import type { Note } from "@/models/entities/note.js"; -import type { User } from "@/models/entities/user.js"; -import type { UserProfile } from "@/models/entities/user-profile.js"; -import { Blockings, Followings, UserProfiles } from "@/models/index.js"; -import { checkWordMute, getFullApAccount, stringToAcct } from "backend-rs"; -import type { Packed } from "@/misc/schema.js"; -import { Cache } from "@/misc/cache.js"; - -const blockingCache = new Cache<User["id"][]>("blocking", 60 * 5); -const hardMutesCache = new Cache<{ - userId: UserProfile["userId"]; - mutedWords: UserProfile["mutedWords"]; - mutedPatterns: UserProfile["mutedPatterns"]; -}>("hardMutes", 60 * 5); -const followingCache = new Cache<User["id"][]>("following", 60 * 5); - -export async function checkHitAntenna( - antenna: Antenna, - note: Note | Packed<"Note">, - noteUser: { id: User["id"]; username: string; host: string | null }, -): Promise<boolean> { - if (note.visibility === "specified") return false; - if (antenna.withFile) { - if (note.fileIds && note.fileIds.length === 0) return false; - } - if (!antenna.withReplies && note.replyId != null) return false; - - if (antenna.src === "users") { - const accts = antenna.users.map((x) => { - const { username, host } = stringToAcct(x); - return getFullApAccount(username, host).toLowerCase(); - }); - if ( - !accts.includes( - getFullApAccount(noteUser.username, noteUser.host).toLowerCase(), - ) - ) - return false; - } else if (antenna.src === "instances") { - const instances = antenna.instances - .filter((x) => x !== "") - .map((host) => { - return host.toLowerCase(); - }); - if (!instances.includes(noteUser.host?.toLowerCase() ?? "")) return false; - } - - const keywords = antenna.keywords - // Clean up - .map((xs) => xs.filter((x) => x !== "")) - .filter((xs) => xs.length > 0); - - let text = `${note.text ?? ""} ${note.cw ?? ""}`; - if (note.files != null) - text += ` ${note.files.map((f) => f.comment ?? "").join(" ")}`; - text = text.trim(); - - if (keywords.length > 0) { - if (note.text == null) return false; - - const matched = keywords.some((and) => - and.every((keyword) => - antenna.caseSensitive - ? text.includes(keyword) - : text.toLowerCase().includes(keyword.toLowerCase()), - ), - ); - - if (!matched) return false; - } - - const excludeKeywords = antenna.excludeKeywords - // Clean up - .map((xs) => xs.filter((x) => x !== "")) - .filter((xs) => xs.length > 0); - - if (excludeKeywords.length > 0) { - if (note.text == null) return false; - - const matched = excludeKeywords.some((and) => - and.every((keyword) => - antenna.caseSensitive - ? note.text?.includes(keyword) - : note.text?.toLowerCase().includes(keyword.toLowerCase()), - ), - ); - - if (matched) return false; - } - - // アンテナ作成者がノート作成者にブロックされていたらスキップ - const blockings = await blockingCache.fetch(noteUser.id, () => - Blockings.findBy({ blockerId: noteUser.id }).then((res) => - res.map((x) => x.blockeeId), - ), - ); - if (blockings.includes(antenna.userId)) return false; - - if (note.visibility === "followers" || note.visibility === "home") { - const following = await followingCache.fetch(antenna.userId, () => - Followings.find({ - where: { followerId: antenna.userId }, - select: ["followeeId"], - }).then((relations) => relations.map((relation) => relation.followeeId)), - ); - if (!following.includes(note.userId)) return false; - } - - const mutes = await hardMutesCache.fetch(antenna.userId, () => - UserProfiles.findOneByOrFail({ - userId: antenna.userId, - }).then((profile) => { - return { - userId: antenna.userId, - mutedWords: profile.mutedWords, - mutedPatterns: profile.mutedPatterns, - }; - }), - ); - if ( - mutes.mutedWords != null && - mutes.mutedPatterns != null && - antenna.userId !== note.userId && - (await checkWordMute(note, mutes.mutedWords, mutes.mutedPatterns)) - ) - return false; - - // TODO: eval expression - - return true; -} diff --git a/packages/backend/src/models/entities/abuse-user-report.ts b/packages/backend/src/models/entities/abuse-user-report.ts index 88f24d130d..37789c91e7 100644 --- a/packages/backend/src/models/entities/abuse-user-report.ts +++ b/packages/backend/src/models/entities/abuse-user-report.ts @@ -16,7 +16,7 @@ export class AbuseUserReport { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the AbuseUserReport.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/access-token.ts b/packages/backend/src/models/entities/access-token.ts index a10cf7a907..e8725b14c3 100644 --- a/packages/backend/src/models/entities/access-token.ts +++ b/packages/backend/src/models/entities/access-token.ts @@ -16,12 +16,12 @@ export class AccessToken { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the AccessToken.", }) public createdAt: Date; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, }) public lastUsedAt: Date | null; diff --git a/packages/backend/src/models/entities/ad.ts b/packages/backend/src/models/entities/ad.ts index a7a630d425..80d54ddd52 100644 --- a/packages/backend/src/models/entities/ad.ts +++ b/packages/backend/src/models/entities/ad.ts @@ -7,13 +7,13 @@ export class Ad { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Ad.", }) public createdAt: Date; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The expired date of the Ad.", }) public expiresAt: Date; diff --git a/packages/backend/src/models/entities/announcement-read.ts b/packages/backend/src/models/entities/announcement-read.ts index 3adff38dab..c32c08c305 100644 --- a/packages/backend/src/models/entities/announcement-read.ts +++ b/packages/backend/src/models/entities/announcement-read.ts @@ -17,7 +17,7 @@ export class AnnouncementRead { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the AnnouncementRead.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/announcement.ts b/packages/backend/src/models/entities/announcement.ts index cd6ae77bc4..7872c0fe1c 100644 --- a/packages/backend/src/models/entities/announcement.ts +++ b/packages/backend/src/models/entities/announcement.ts @@ -7,12 +7,12 @@ export class Announcement { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Announcement.", }) public createdAt: Date; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The updated date of the Announcement.", nullable: true, }) diff --git a/packages/backend/src/models/entities/antenna.ts b/packages/backend/src/models/entities/antenna.ts index 7f3bea0be8..741a8f18a3 100644 --- a/packages/backend/src/models/entities/antenna.ts +++ b/packages/backend/src/models/entities/antenna.ts @@ -17,7 +17,7 @@ export class Antenna { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Antenna.", }) public createdAt: Date; @@ -59,20 +59,30 @@ export class Antenna { }) public users: string[]; - @Column("jsonb", { - default: [], + @Column("varchar", { + length: 512, + array: true, + default: "{}", }) public instances: string[]; - @Column("jsonb", { - default: [], + // whitespace: AND condition + // array items: OR condition + // e.g., ["alpha beta", "gamma"] + // does match "alpha beta", "beta alpha alpha", "gamma alpha", "gamma epsilon" + // does not match "alpha", "beta gamma", "alpha alpha", "eplison" + @Column("text", { + array: true, + default: "{}", }) - public keywords: string[][]; + public keywords: string[]; - @Column("jsonb", { - default: [], + // same match rule as `keywords`, except that this field is for excluded words + @Column("text", { + array: true, + default: "{}", }) - public excludeKeywords: string[][]; + public excludeKeywords: string[]; @Column("boolean", { default: false, diff --git a/packages/backend/src/models/entities/app.ts b/packages/backend/src/models/entities/app.ts index cddfb18ee3..f155e187fa 100644 --- a/packages/backend/src/models/entities/app.ts +++ b/packages/backend/src/models/entities/app.ts @@ -15,7 +15,7 @@ export class App { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the App.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/attestation-challenge.ts b/packages/backend/src/models/entities/attestation-challenge.ts index 9121ef5368..5c91e6dc1d 100644 --- a/packages/backend/src/models/entities/attestation-challenge.ts +++ b/packages/backend/src/models/entities/attestation-challenge.ts @@ -26,7 +26,7 @@ export class AttestationChallenge { }) public challenge: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The date challenge was created for expiry purposes.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/auth-session.ts b/packages/backend/src/models/entities/auth-session.ts index 9f86709889..f2ec7a402c 100644 --- a/packages/backend/src/models/entities/auth-session.ts +++ b/packages/backend/src/models/entities/auth-session.ts @@ -16,7 +16,7 @@ export class AuthSession { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the AuthSession.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/blocking.ts b/packages/backend/src/models/entities/blocking.ts index b530834075..828a5550f7 100644 --- a/packages/backend/src/models/entities/blocking.ts +++ b/packages/backend/src/models/entities/blocking.ts @@ -17,7 +17,7 @@ export class Blocking { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Blocking.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/channel-following.ts b/packages/backend/src/models/entities/channel-following.ts index 7314366636..b1727ecc9c 100644 --- a/packages/backend/src/models/entities/channel-following.ts +++ b/packages/backend/src/models/entities/channel-following.ts @@ -18,7 +18,7 @@ export class ChannelFollowing { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the ChannelFollowing.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/channel-note-pining.ts b/packages/backend/src/models/entities/channel-note-pining.ts index 7061b83504..ea3bcf67e5 100644 --- a/packages/backend/src/models/entities/channel-note-pining.ts +++ b/packages/backend/src/models/entities/channel-note-pining.ts @@ -17,7 +17,7 @@ export class ChannelNotePining { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the ChannelNotePining.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/channel.ts b/packages/backend/src/models/entities/channel.ts index 99cd3d283f..425398d446 100644 --- a/packages/backend/src/models/entities/channel.ts +++ b/packages/backend/src/models/entities/channel.ts @@ -17,13 +17,13 @@ export class Channel { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Channel.", }) public createdAt: Date; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, }) public lastNotedAt: Date | null; diff --git a/packages/backend/src/models/entities/clip.ts b/packages/backend/src/models/entities/clip.ts index 389d008f74..90388ac087 100644 --- a/packages/backend/src/models/entities/clip.ts +++ b/packages/backend/src/models/entities/clip.ts @@ -15,7 +15,7 @@ export class Clip { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Clip.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/drive-file.ts b/packages/backend/src/models/entities/drive-file.ts index e3a7d1c370..b5fc632be1 100644 --- a/packages/backend/src/models/entities/drive-file.ts +++ b/packages/backend/src/models/entities/drive-file.ts @@ -23,7 +23,7 @@ export class DriveFile { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the DriveFile.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/drive-folder.ts b/packages/backend/src/models/entities/drive-folder.ts index 17580f3c8d..1d6b24044d 100644 --- a/packages/backend/src/models/entities/drive-folder.ts +++ b/packages/backend/src/models/entities/drive-folder.ts @@ -16,7 +16,7 @@ export class DriveFolder { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the DriveFolder.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/emoji.ts b/packages/backend/src/models/entities/emoji.ts index 1f40052389..87b525dc59 100644 --- a/packages/backend/src/models/entities/emoji.ts +++ b/packages/backend/src/models/entities/emoji.ts @@ -7,7 +7,7 @@ export class Emoji { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, }) public updatedAt: Date | null; diff --git a/packages/backend/src/models/entities/follow-request.ts b/packages/backend/src/models/entities/follow-request.ts index 1631d8c965..615de69b21 100644 --- a/packages/backend/src/models/entities/follow-request.ts +++ b/packages/backend/src/models/entities/follow-request.ts @@ -16,7 +16,7 @@ export class FollowRequest { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the FollowRequest.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/following.ts b/packages/backend/src/models/entities/following.ts index 855b9f1745..350980b550 100644 --- a/packages/backend/src/models/entities/following.ts +++ b/packages/backend/src/models/entities/following.ts @@ -17,7 +17,7 @@ export class Following { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Following.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/gallery-like.ts b/packages/backend/src/models/entities/gallery-like.ts index f8ec098f68..80bac095d8 100644 --- a/packages/backend/src/models/entities/gallery-like.ts +++ b/packages/backend/src/models/entities/gallery-like.ts @@ -17,7 +17,7 @@ export class GalleryLike { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone") + @Column("timestamp with time zone") public createdAt: Date; @Index() diff --git a/packages/backend/src/models/entities/gallery-post.ts b/packages/backend/src/models/entities/gallery-post.ts index 711e7a7860..5152f75717 100644 --- a/packages/backend/src/models/entities/gallery-post.ts +++ b/packages/backend/src/models/entities/gallery-post.ts @@ -17,13 +17,13 @@ export class GalleryPost { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the GalleryPost.", }) public createdAt: Date; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The updated date of the GalleryPost.", }) public updatedAt: Date; diff --git a/packages/backend/src/models/entities/instance.ts b/packages/backend/src/models/entities/instance.ts index afdedd2d3f..daeadaec8f 100644 --- a/packages/backend/src/models/entities/instance.ts +++ b/packages/backend/src/models/entities/instance.ts @@ -23,7 +23,7 @@ export class Instance { * このインスタンスを捕捉した日時 */ @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The caught date of the Instance.", }) public caughtAt: Date; @@ -75,7 +75,7 @@ export class Instance { /** * 直近のリクエスト送信日時 */ - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, }) public latestRequestSentAt: Date | null; @@ -91,7 +91,7 @@ export class Instance { /** * 直近のリクエスト受信日時 */ - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, }) public latestRequestReceivedAt: Date | null; @@ -99,7 +99,7 @@ export class Instance { /** * このインスタンスと最後にやり取りした日時 */ - @Column("timestamp without time zone") + @Column("timestamp with time zone") public lastCommunicatedAt: Date; /** @@ -179,7 +179,7 @@ export class Instance { }) public themeColor: string | null; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, }) public infoUpdatedAt: Date | null; diff --git a/packages/backend/src/models/entities/messaging-message.ts b/packages/backend/src/models/entities/messaging-message.ts index 5c9c17a027..039811887c 100644 --- a/packages/backend/src/models/entities/messaging-message.ts +++ b/packages/backend/src/models/entities/messaging-message.ts @@ -18,7 +18,7 @@ export class MessagingMessage { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the MessagingMessage.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/moderation-log.ts b/packages/backend/src/models/entities/moderation-log.ts index 6eb3ec27ff..a2081d2f5e 100644 --- a/packages/backend/src/models/entities/moderation-log.ts +++ b/packages/backend/src/models/entities/moderation-log.ts @@ -15,7 +15,7 @@ export class ModerationLog { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the ModerationLog.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/muting.ts b/packages/backend/src/models/entities/muting.ts index dd6ee0bf44..fac613d790 100644 --- a/packages/backend/src/models/entities/muting.ts +++ b/packages/backend/src/models/entities/muting.ts @@ -17,13 +17,13 @@ export class Muting { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Muting.", }) public createdAt: Date; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, }) public expiresAt: Date | null; diff --git a/packages/backend/src/models/entities/note-edit.ts b/packages/backend/src/models/entities/note-edit.ts index aed85c7614..18d0e19003 100644 --- a/packages/backend/src/models/entities/note-edit.ts +++ b/packages/backend/src/models/entities/note-edit.ts @@ -40,7 +40,7 @@ export class NoteEdit { }) public fileIds: DriveFile["id"][]; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The updated date of the Note.", }) public updatedAt: Date; diff --git a/packages/backend/src/models/entities/note-favorite.ts b/packages/backend/src/models/entities/note-favorite.ts index 200655e929..5915af842d 100644 --- a/packages/backend/src/models/entities/note-favorite.ts +++ b/packages/backend/src/models/entities/note-favorite.ts @@ -17,7 +17,7 @@ export class NoteFavorite { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the NoteFavorite.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/note-reaction.ts b/packages/backend/src/models/entities/note-reaction.ts index 1d2fc567b5..8dfa6ad28e 100644 --- a/packages/backend/src/models/entities/note-reaction.ts +++ b/packages/backend/src/models/entities/note-reaction.ts @@ -17,7 +17,7 @@ export class NoteReaction { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the NoteReaction.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/note-thread-muting.ts b/packages/backend/src/models/entities/note-thread-muting.ts index 9566d6d89f..c20d0f41e3 100644 --- a/packages/backend/src/models/entities/note-thread-muting.ts +++ b/packages/backend/src/models/entities/note-thread-muting.ts @@ -16,7 +16,7 @@ export class NoteThreadMuting { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", {}) + @Column("timestamp with time zone", {}) public createdAt: Date; @Index() diff --git a/packages/backend/src/models/entities/note-watching.ts b/packages/backend/src/models/entities/note-watching.ts index 37b48b75b8..b7473c9135 100644 --- a/packages/backend/src/models/entities/note-watching.ts +++ b/packages/backend/src/models/entities/note-watching.ts @@ -18,7 +18,7 @@ export class NoteWatching { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the NoteWatching.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/note.ts b/packages/backend/src/models/entities/note.ts index 3b7315288e..1c9d3570a3 100644 --- a/packages/backend/src/models/entities/note.ts +++ b/packages/backend/src/models/entities/note.ts @@ -26,7 +26,7 @@ export class Note { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Note.", }) public createdAt: Date; @@ -238,7 +238,7 @@ export class Note { }) public renoteUserHost: string | null; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, comment: "The updated date of the Note.", }) diff --git a/packages/backend/src/models/entities/notification.ts b/packages/backend/src/models/entities/notification.ts index 57a2a59158..6c3ec5bbd2 100644 --- a/packages/backend/src/models/entities/notification.ts +++ b/packages/backend/src/models/entities/notification.ts @@ -20,7 +20,7 @@ export class Notification { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Notification.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/page-like.ts b/packages/backend/src/models/entities/page-like.ts index fba142375d..0ec17e34e0 100644 --- a/packages/backend/src/models/entities/page-like.ts +++ b/packages/backend/src/models/entities/page-like.ts @@ -17,7 +17,7 @@ export class PageLike { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone") + @Column("timestamp with time zone") public createdAt: Date; @Index() diff --git a/packages/backend/src/models/entities/page.ts b/packages/backend/src/models/entities/page.ts index af8e095dd5..7120b289c1 100644 --- a/packages/backend/src/models/entities/page.ts +++ b/packages/backend/src/models/entities/page.ts @@ -18,13 +18,13 @@ export class Page { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Page.", }) public createdAt: Date; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The updated date of the Page.", }) public updatedAt: Date; diff --git a/packages/backend/src/models/entities/password-reset-request.ts b/packages/backend/src/models/entities/password-reset-request.ts index 23ec16e38b..048c45b0c8 100644 --- a/packages/backend/src/models/entities/password-reset-request.ts +++ b/packages/backend/src/models/entities/password-reset-request.ts @@ -15,7 +15,7 @@ export class PasswordResetRequest { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone") + @Column("timestamp with time zone") public createdAt: Date; @Index({ unique: true }) diff --git a/packages/backend/src/models/entities/poll-vote.ts b/packages/backend/src/models/entities/poll-vote.ts index 7b9cc19c3c..40a0d9ae07 100644 --- a/packages/backend/src/models/entities/poll-vote.ts +++ b/packages/backend/src/models/entities/poll-vote.ts @@ -18,7 +18,7 @@ export class PollVote { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the PollVote.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/poll.ts b/packages/backend/src/models/entities/poll.ts index 9ef2091566..2c5f556b4d 100644 --- a/packages/backend/src/models/entities/poll.ts +++ b/packages/backend/src/models/entities/poll.ts @@ -17,7 +17,7 @@ export class Poll { @PrimaryColumn(id()) public noteId: Note["id"]; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, }) public expiresAt: Date | null; diff --git a/packages/backend/src/models/entities/promo-note.ts b/packages/backend/src/models/entities/promo-note.ts index 18ff3ac63c..54424f4339 100644 --- a/packages/backend/src/models/entities/promo-note.ts +++ b/packages/backend/src/models/entities/promo-note.ts @@ -16,7 +16,7 @@ export class PromoNote { @PrimaryColumn(id()) public noteId: Note["id"]; - @Column("timestamp without time zone") + @Column("timestamp with time zone") public expiresAt: Date; //#region Denormalized fields diff --git a/packages/backend/src/models/entities/promo-read.ts b/packages/backend/src/models/entities/promo-read.ts index efda0757c6..a28c22f8f1 100644 --- a/packages/backend/src/models/entities/promo-read.ts +++ b/packages/backend/src/models/entities/promo-read.ts @@ -17,7 +17,7 @@ export class PromoRead { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the PromoRead.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/registration-tickets.ts b/packages/backend/src/models/entities/registration-tickets.ts index db0a416550..549f05d07a 100644 --- a/packages/backend/src/models/entities/registration-tickets.ts +++ b/packages/backend/src/models/entities/registration-tickets.ts @@ -6,7 +6,7 @@ export class RegistrationTicket { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone") + @Column("timestamp with time zone") public createdAt: Date; @Index({ unique: true }) diff --git a/packages/backend/src/models/entities/registry-item.ts b/packages/backend/src/models/entities/registry-item.ts index e9e54b7122..d143ba1df8 100644 --- a/packages/backend/src/models/entities/registry-item.ts +++ b/packages/backend/src/models/entities/registry-item.ts @@ -16,12 +16,12 @@ export class RegistryItem { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the RegistryItem.", }) public createdAt: Date; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The updated date of the RegistryItem.", }) public updatedAt: Date; diff --git a/packages/backend/src/models/entities/renote-muting.ts b/packages/backend/src/models/entities/renote-muting.ts index 42cb14127b..86c560d32d 100644 --- a/packages/backend/src/models/entities/renote-muting.ts +++ b/packages/backend/src/models/entities/renote-muting.ts @@ -17,7 +17,7 @@ export class RenoteMuting { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Muting.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/reply-muting.ts b/packages/backend/src/models/entities/reply-muting.ts index e48463e683..22cfea4e7d 100644 --- a/packages/backend/src/models/entities/reply-muting.ts +++ b/packages/backend/src/models/entities/reply-muting.ts @@ -17,7 +17,7 @@ export class ReplyMuting { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Muting.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/signin.ts b/packages/backend/src/models/entities/signin.ts index 89d50a6b5f..7137cdb946 100644 --- a/packages/backend/src/models/entities/signin.ts +++ b/packages/backend/src/models/entities/signin.ts @@ -15,7 +15,7 @@ export class Signin { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Signin.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/sw-subscription.ts b/packages/backend/src/models/entities/sw-subscription.ts index a4a6ae7711..c1837e329e 100644 --- a/packages/backend/src/models/entities/sw-subscription.ts +++ b/packages/backend/src/models/entities/sw-subscription.ts @@ -15,7 +15,7 @@ export class SwSubscription { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone") + @Column("timestamp with time zone") public createdAt: Date; @Index() diff --git a/packages/backend/src/models/entities/used-username.ts b/packages/backend/src/models/entities/used-username.ts index 4504301b14..d00a25991e 100644 --- a/packages/backend/src/models/entities/used-username.ts +++ b/packages/backend/src/models/entities/used-username.ts @@ -7,7 +7,7 @@ export class UsedUsername { }) public username: string; - @Column("timestamp without time zone") + @Column("timestamp with time zone") public createdAt: Date; constructor(data: Partial<UsedUsername>) { diff --git a/packages/backend/src/models/entities/user-group-invitation.ts b/packages/backend/src/models/entities/user-group-invitation.ts index be34fdc757..1fdfb96b5c 100644 --- a/packages/backend/src/models/entities/user-group-invitation.ts +++ b/packages/backend/src/models/entities/user-group-invitation.ts @@ -17,7 +17,7 @@ export class UserGroupInvitation { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the UserGroupInvitation.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/user-group-joining.ts b/packages/backend/src/models/entities/user-group-joining.ts index d7af03bf8f..598d6ebf4a 100644 --- a/packages/backend/src/models/entities/user-group-joining.ts +++ b/packages/backend/src/models/entities/user-group-joining.ts @@ -17,7 +17,7 @@ export class UserGroupJoining { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the UserGroupJoining.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/user-group.ts b/packages/backend/src/models/entities/user-group.ts index 801b4e619f..379af8be05 100644 --- a/packages/backend/src/models/entities/user-group.ts +++ b/packages/backend/src/models/entities/user-group.ts @@ -16,7 +16,7 @@ export class UserGroup { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the UserGroup.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/user-ip.ts b/packages/backend/src/models/entities/user-ip.ts index 7c43816b66..adef48e4c4 100644 --- a/packages/backend/src/models/entities/user-ip.ts +++ b/packages/backend/src/models/entities/user-ip.ts @@ -8,7 +8,7 @@ export class UserIp { @PrimaryGeneratedColumn() public id: string; - @Column("timestamp without time zone", {}) + @Column("timestamp with time zone", {}) public createdAt: Date; @Index() diff --git a/packages/backend/src/models/entities/user-list-joining.ts b/packages/backend/src/models/entities/user-list-joining.ts index bd3ef8bde1..2089a072cc 100644 --- a/packages/backend/src/models/entities/user-list-joining.ts +++ b/packages/backend/src/models/entities/user-list-joining.ts @@ -17,7 +17,7 @@ export class UserListJoining { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the UserListJoining.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/user-list.ts b/packages/backend/src/models/entities/user-list.ts index 6f192c6bab..a1496bfde2 100644 --- a/packages/backend/src/models/entities/user-list.ts +++ b/packages/backend/src/models/entities/user-list.ts @@ -15,7 +15,7 @@ export class UserList { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the UserList.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/user-note-pining.ts b/packages/backend/src/models/entities/user-note-pining.ts index d262c3f5b3..053abde91c 100644 --- a/packages/backend/src/models/entities/user-note-pining.ts +++ b/packages/backend/src/models/entities/user-note-pining.ts @@ -17,7 +17,7 @@ export class UserNotePining { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the UserNotePinings.", }) public createdAt: Date; diff --git a/packages/backend/src/models/entities/user-pending.ts b/packages/backend/src/models/entities/user-pending.ts index 1383c4d4da..18ae5ad993 100644 --- a/packages/backend/src/models/entities/user-pending.ts +++ b/packages/backend/src/models/entities/user-pending.ts @@ -6,7 +6,7 @@ export class UserPending { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone") + @Column("timestamp with time zone") public createdAt: Date; @Index({ unique: true }) diff --git a/packages/backend/src/models/entities/user-profile.ts b/packages/backend/src/models/entities/user-profile.ts index 2b9ac79195..4408384768 100644 --- a/packages/backend/src/models/entities/user-profile.ts +++ b/packages/backend/src/models/entities/user-profile.ts @@ -138,20 +138,6 @@ export class UserProfile { }) public moderationNote: string | null; - // TODO: そのうち消す - @Column("jsonb", { - default: {}, - comment: "The client-specific data of the User.", - }) - public clientData: Record<string, any>; - - // TODO: そのうち消す - @Column("jsonb", { - default: {}, - comment: "The room data of the User.", - }) - public room: Record<string, any>; - @Column("boolean", { default: false, }) @@ -200,12 +186,6 @@ export class UserProfile { }) public pinnedPageId: Page["id"] | null; - @OneToOne((type) => Page, { - onDelete: "SET NULL", - }) - @JoinColumn() - public pinnedPage: Page | null; - @Index() @Column("boolean", { default: false, @@ -213,19 +193,28 @@ export class UserProfile { }) public enableWordMute: boolean; - @Column("jsonb", { - default: [], + // whitespace: AND condition + // array items: OR condition + // e.g., ["alpha beta", "gamma"] + // does match "alpha beta", "beta alpha alpha", "gamma alpha", "gamma epsilon" + // does not match "alpha", "beta gamma", "alpha alpha", "eplison" + @Column("text", { + array: true, + default: "{}", }) - public mutedWords: string[][]; + public mutedWords: string[]; + // array of regular expressions @Column("text", { array: true, nullable: false, }) public mutedPatterns: string[]; - @Column("jsonb", { - default: [], + @Column("varchar", { + length: 512, + array: true, + default: "{}", comment: "List of instances muted by the user.", }) public mutedInstances: string[]; @@ -253,6 +242,13 @@ export class UserProfile { }) @JoinColumn() public user: Relation<User>; + + @OneToOne(() => Page, { + onDelete: "SET NULL", + nullable: true, + }) + @JoinColumn() + public pinnedPage: Relation<Page | null>; //#endregion constructor(data: Partial<UserProfile>) { diff --git a/packages/backend/src/models/entities/user-security-key.ts b/packages/backend/src/models/entities/user-security-key.ts index b2853f0aa9..ead900be06 100644 --- a/packages/backend/src/models/entities/user-security-key.ts +++ b/packages/backend/src/models/entities/user-security-key.ts @@ -28,7 +28,7 @@ export class UserSecurityKey { }) public publicKey: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The date of the last time the UserSecurityKey was successfully validated.", }) diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index ad86e72422..65780eb44d 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -19,25 +19,25 @@ export class User { public id: string; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the User.", }) public createdAt: Date; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, comment: "The updated date of the User.", }) public updatedAt: Date | null; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, }) public lastFetchedAt: Date | null; @Index() - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, }) public lastActiveDate: Date | null; diff --git a/packages/backend/src/models/entities/webhook.ts b/packages/backend/src/models/entities/webhook.ts index bc48e537ae..8f3fa1d82c 100644 --- a/packages/backend/src/models/entities/webhook.ts +++ b/packages/backend/src/models/entities/webhook.ts @@ -26,7 +26,7 @@ export class Webhook { @PrimaryColumn(id()) public id: string; - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { comment: "The created date of the Antenna.", }) public createdAt: Date; @@ -71,7 +71,7 @@ export class Webhook { /** * 直近のリクエスト送信日時 */ - @Column("timestamp without time zone", { + @Column("timestamp with time zone", { nullable: true, }) public latestSentAt: Date | null; diff --git a/packages/backend/src/models/repositories/antenna.ts b/packages/backend/src/models/repositories/antenna.ts index 6917edb5b7..e6e62bbc10 100644 --- a/packages/backend/src/models/repositories/antenna.ts +++ b/packages/backend/src/models/repositories/antenna.ts @@ -16,8 +16,8 @@ export const AntennaRepository = db.getRepository(Antenna).extend({ id: antenna.id, createdAt: antenna.createdAt.toISOString(), name: antenna.name, - keywords: antenna.keywords, - excludeKeywords: antenna.excludeKeywords, + keywords: antenna.keywords.map((row) => row.split(" ")), + excludeKeywords: antenna.excludeKeywords.map((row) => row.split(" ")), src: antenna.src, userListId: antenna.userListId, userGroupId: userGroupJoining ? userGroupJoining.userGroupId : null, diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 0f4ea9f72a..c07473d0ea 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -573,7 +573,7 @@ export const UserRepository = db.getRepository(User).extend({ hasUnreadNotification: this.getHasUnreadNotification(user.id), hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), - mutedWords: profile?.mutedWords, + mutedWords: profile?.mutedWords.map((row) => row.split(" ")), mutedPatterns: profile?.mutedPatterns, mutedInstances: profile?.mutedInstances, mutingNotificationTypes: profile?.mutingNotificationTypes, diff --git a/packages/backend/src/server/api/common/generate-muted-user-query.ts b/packages/backend/src/server/api/common/generate-muted-user-query.ts index 3538fbf0af..557c884d94 100644 --- a/packages/backend/src/server/api/common/generate-muted-user-query.ts +++ b/packages/backend/src/server/api/common/generate-muted-user-query.ts @@ -42,25 +42,13 @@ export function generateMutedUserQuery( ) // mute instances .andWhere( - new Brackets((qb) => { - qb.andWhere("note.userHost IS NULL").orWhere( - `NOT ((${mutingInstanceQuery.getQuery()})::jsonb ? note.userHost)`, - ); - }), - ) - .andWhere( - new Brackets((qb) => { - qb.where("note.replyUserHost IS NULL").orWhere( - `NOT ((${mutingInstanceQuery.getQuery()})::jsonb ? note.replyUserHost)`, - ); - }), - ) - .andWhere( - new Brackets((qb) => { - qb.where("note.renoteUserHost IS NULL").orWhere( - `NOT ((${mutingInstanceQuery.getQuery()})::jsonb ? note.renoteUserHost)`, - ); - }), + `NOT + ARRAY[ + note."userHost", + note."replyUserHost", + note."renoteUserHost" + ]::character varying[] + && (${mutingInstanceQuery.getQuery()})`, ); q.setParameters(mutingQuery.getParameters()); diff --git a/packages/backend/src/server/api/common/generated-muted-renote-query.ts b/packages/backend/src/server/api/common/generated-muted-renote-query.ts index 3fcd9b28e8..b80d53c92f 100644 --- a/packages/backend/src/server/api/common/generated-muted-renote-query.ts +++ b/packages/backend/src/server/api/common/generated-muted-renote-query.ts @@ -1,5 +1,5 @@ -import { Brackets, SelectQueryBuilder } from "typeorm"; -import { User } from "@/models/entities/user.js"; +import { Brackets, type SelectQueryBuilder } from "typeorm"; +import type { User } from "@/models/entities/user.js"; import { RenoteMutings } from "@/models/index.js"; export function generateMutedUserRenotesQueryForNotes( diff --git a/packages/backend/src/server/api/common/generated-muted-reply-query.ts b/packages/backend/src/server/api/common/generated-muted-reply-query.ts index 6aff5ebd21..6cffbdb00a 100644 --- a/packages/backend/src/server/api/common/generated-muted-reply-query.ts +++ b/packages/backend/src/server/api/common/generated-muted-reply-query.ts @@ -1,5 +1,5 @@ -import { Brackets, SelectQueryBuilder } from "typeorm"; -import { User } from "@/models/entities/user.js"; +import { Brackets, type SelectQueryBuilder } from "typeorm"; +import type { User } from "@/models/entities/user.js"; import { ReplyMutings } from "@/models/index.js"; export function generateMutedUserRepliesQueryForNotes( diff --git a/packages/backend/src/server/api/common/read-messaging-message.ts b/packages/backend/src/server/api/common/read-messaging-message.ts index 2a1667f167..e8706fe86a 100644 --- a/packages/backend/src/server/api/common/read-messaging-message.ts +++ b/packages/backend/src/server/api/common/read-messaging-message.ts @@ -57,13 +57,15 @@ export async function readUserMessagingMessage( ); // Publish event - publishToChatStream(otherpartyId, userId, ChatEvent.Read, messageIds); - publishToChatIndexStream(userId, ChatIndexEvent.Read, messageIds); + await Promise.all([ + publishToChatStream(otherpartyId, userId, ChatEvent.Read, messageIds), + publishToChatIndexStream(userId, ChatIndexEvent.Read, messageIds), + ]); if (!(await Users.getHasUnreadMessagingMessage(userId))) { // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行 publishMainStream(userId, "readAllMessagingMessages"); - sendPushNotification(userId, PushNotificationKind.ReadAllChats, {}); + await sendPushNotification(userId, PushNotificationKind.ReadAllChats, {}); } else { // そのユーザーとのメッセージで未読がなければイベント発行 const hasUnread = await MessagingMessages.exists({ @@ -75,9 +77,13 @@ export async function readUserMessagingMessage( }); if (!hasUnread) { - sendPushNotification(userId, PushNotificationKind.ReadAllChatsInTheRoom, { - userId: otherpartyId, - }); + await sendPushNotification( + userId, + PushNotificationKind.ReadAllChatsInTheRoom, + { + userId: otherpartyId, + }, + ); } } } @@ -127,17 +133,19 @@ export async function readGroupMessagingMessage( reads.push(message.id); } - // Publish event - publishToGroupChatStream(groupId, ChatEvent.Read, { - ids: reads, - userId, - }); - publishToChatIndexStream(userId, ChatIndexEvent.Read, reads); + // Publish events + await Promise.all([ + publishToGroupChatStream(groupId, ChatEvent.Read, { + ids: reads, + userId, + }), + publishToChatIndexStream(userId, ChatIndexEvent.Read, reads), + ]); if (!(await Users.getHasUnreadMessagingMessage(userId))) { // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行 publishMainStream(userId, "readAllMessagingMessages"); - sendPushNotification(userId, PushNotificationKind.ReadAllChats, {}); + await sendPushNotification(userId, PushNotificationKind.ReadAllChats, {}); } else { // そのグループにおいて未読がなければイベント発行 const hasUnread = await MessagingMessages.createQueryBuilder("message") @@ -151,9 +159,13 @@ export async function readGroupMessagingMessage( .then((x) => x != null); if (!hasUnread) { - sendPushNotification(userId, PushNotificationKind.ReadAllChatsInTheRoom, { - groupId, - }); + await sendPushNotification( + userId, + PushNotificationKind.ReadAllChatsInTheRoom, + { + groupId, + }, + ); } } } diff --git a/packages/backend/src/server/api/common/read-notification.ts b/packages/backend/src/server/api/common/read-notification.ts index 5237406df1..b3fdf31ced 100644 --- a/packages/backend/src/server/api/common/read-notification.ts +++ b/packages/backend/src/server/api/common/read-notification.ts @@ -26,8 +26,8 @@ export async function readNotification( if (result.affected === 0) return; if (!(await Users.getHasUnreadNotification(userId))) - return postReadAllNotifications(userId); - else return postReadNotifications(userId, notificationIds); + return await postReadAllNotifications(userId); + else return await postReadNotifications(userId, notificationIds); } export async function readNotificationByQuery( diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 59139904bc..7b10ad99b1 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -77,9 +77,10 @@ export default define(meta, paramDef, async (ps, me) => { height: size?.height || null, }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); - await db.queryResultCache!.remove(["meta_emojis"]); - - publishToBroadcastStream(await Emojis.pack(emoji)); + await Promise.all([ + db.queryResultCache!.remove(["meta_emojis"]), + publishToBroadcastStream(await Emojis.pack(emoji)), + ]); insertModerationLog(me, "addEmoji", { emojiId: emoji.id, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 9b08076b35..8160bf5bf7 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -104,9 +104,10 @@ export default define(meta, paramDef, async (ps, me) => { height: size?.height ?? null, }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); - await db.queryResultCache!.remove(["meta_emojis"]); - - publishToBroadcastStream(await Emojis.pack(copied)); + await Promise.all([ + db.queryResultCache!.remove(["meta_emojis"]), + publishToBroadcastStream(await Emojis.pack(copied)), + ]); return { id: copied.id, diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 3ad255ddde..016a08ab51 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -59,7 +59,7 @@ export default define(meta, paramDef, async (ps, me) => { carefulBot: profile.carefulBot, injectFeaturedNote: profile.injectFeaturedNote, receiveAnnouncementEmail: profile.receiveAnnouncementEmail, - mutedWords: profile.mutedWords, + mutedWords: profile.mutedWords.map((row) => row.split(" ")), mutedPatterns: profile.mutedPatterns, mutedInstances: profile.mutedInstances, mutingNotificationTypes: profile.mutingNotificationTypes, diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index aa5dcee044..76842df529 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -104,8 +104,22 @@ export const paramDef = { } as const; export default define(meta, paramDef, async (ps, user) => { + const flatten = (arr: string[][]) => + JSON.stringify(arr) === "[[]]" + ? ([] as string[]) + : arr.map((row) => row.join(" ")); + + const keywords = flatten( + ps.keywords.map((row) => row.filter((word) => word.trim().length > 0)), + ); + const excludedWords = flatten( + ps.excludeKeywords.map((row) => + row.filter((word) => word.trim().length > 0), + ), + ); + if (user.movedToUri != null) throw new ApiError(meta.errors.noSuchUserGroup); - if (ps.keywords.length === 0) throw new ApiError(meta.errors.noKeywords); + if (keywords.length === 0) throw new ApiError(meta.errors.noKeywords); let userList; let userGroupJoining; @@ -146,10 +160,10 @@ export default define(meta, paramDef, async (ps, user) => { src: ps.src, userListId: userList ? userList.id : null, userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null, - keywords: ps.keywords, - excludeKeywords: ps.excludeKeywords, + keywords: keywords, + excludeKeywords: excludedWords, users: ps.users, - instances: ps.instances, + instances: ps.instances.filter((instance) => instance.trim().length > 0), caseSensitive: ps.caseSensitive, withReplies: ps.withReplies, withFile: ps.withFile, diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 9a71a8f9ff..5e74f4d6b2 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -100,6 +100,20 @@ export const paramDef = { } as const; export default define(meta, paramDef, async (ps, user) => { + const flatten = (arr: string[][]) => + JSON.stringify(arr) === "[[]]" + ? ([] as string[]) + : arr.map((row) => row.join(" ")); + + const keywords = flatten( + ps.keywords.map((row) => row.filter((word) => word.trim().length > 0)), + ); + const excludedWords = flatten( + ps.excludeKeywords.map((row) => + row.filter((word) => word.trim().length > 0), + ), + ); + // Fetch the antenna const antenna = await Antennas.findOneBy({ id: ps.antennaId, @@ -138,10 +152,10 @@ export default define(meta, paramDef, async (ps, user) => { src: ps.src, userListId: userList ? userList.id : null, userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null, - keywords: ps.keywords, - excludeKeywords: ps.excludeKeywords, + keywords: keywords, + excludeKeywords: excludedWords, users: ps.users, - instances: ps.instances, + instances: ps.instances.filter((instance) => instance.trim().length > 0), caseSensitive: ps.caseSensitive, withReplies: ps.withReplies, withFile: ps.withFile, diff --git a/packages/backend/src/server/api/endpoints/i/known-as.ts b/packages/backend/src/server/api/endpoints/i/known-as.ts index 6d1c340bca..62b1cd96ad 100644 --- a/packages/backend/src/server/api/endpoints/i/known-as.ts +++ b/packages/backend/src/server/api/endpoints/i/known-as.ts @@ -102,7 +102,7 @@ export default define(meta, paramDef, async (ps, user) => { acceptAllFollowRequests(user); } - publishToFollowers(user.id); + await publishToFollowers(user.id); return iObj; }); diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index b07247dd83..cef15c3993 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -125,7 +125,8 @@ export default define(meta, paramDef, async (ps, user) => { query.andWhere( new Brackets((qb) => { qb.andWhere("notifier.host IS NULL").orWhere( - `NOT (( ${mutingInstanceQuery.getQuery()} )::jsonb ? notifier.host)`, + `NOT EXISTS (SELECT 1 FROM "user_profile" WHERE "userId" = :muterId AND notifier.host = ANY("mutedInstances"))`, + { muterId: user.id }, ); }), ); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 50f0da8f8d..98d760ddb5 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -178,26 +178,11 @@ export default define(meta, paramDef, async (ps, _user, token) => { } } if (ps.mutedWords !== undefined) { - // for backward compatibility - for (const item of ps.mutedWords) { - if (Array.isArray(item)) continue; - - const regexp = item.match(/^\/(.+)\/(.*)$/); - if (!regexp) throw new ApiError(meta.errors.invalidRegexp); - - try { - new RegExp(regexp[1], regexp[2]); - } catch (err) { - throw new ApiError(meta.errors.invalidRegexp); - } - - profileUpdates.mutedPatterns = profileUpdates.mutedPatterns ?? []; - profileUpdates.mutedPatterns.push(item); - } - - profileUpdates.mutedWords = ps.mutedWords.filter((item) => - Array.isArray(item), - ); + const flatten = (arr: string[][]) => + JSON.stringify(arr) === "[[]]" + ? ([] as string[]) + : arr.map((row) => row.join(" ")); + profileUpdates.mutedWords = flatten(ps.mutedWords); } if ( profileUpdates.mutedWords !== undefined || @@ -366,11 +351,11 @@ export default define(meta, paramDef, async (ps, _user, token) => { // 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認 if (user.isLocked && ps.isLocked === false) { - acceptAllFollowRequests(user); + await acceptAllFollowRequests(user); } // フォロワーにUpdateを配信 - publishToFollowers(user.id); + await publishToFollowers(user.id); return iObj; }); diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts index ffb8d8c2c4..0b5f65eabd 100644 --- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts @@ -31,5 +31,9 @@ export default define(meta, paramDef, async (_, user) => { // 全ての通知を読みましたよというイベントを発行 publishMainStream(user.id, "readAllNotifications"); - sendPushNotification(user.id, PushNotificationKind.ReadAllNotifications, {}); + await sendPushNotification( + user.id, + PushNotificationKind.ReadAllNotifications, + {}, + ); }); diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index f43cd8ae0f..8a134cf9e7 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -84,8 +84,8 @@ export default define(meta, paramDef, async (ps, me) => { ], }); - for (const moderator of moderators) { - publishToModerationStream(moderator.id, { + for await (const moderator of moderators) { + await publishToModerationStream(moderator.id, { id: report.id, targetUserId: report.targetUserId, reporterId: report.reporterId, diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index 3bb0ca073b..8e77677105 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -287,10 +287,10 @@ export default class Connection { // クライアントの事情を考慮したとき、入力フォームはノートチャンネルやメッセージのメインコンポーネントとは別 // なこともあるため、それらのコンポーネントがそれぞれ各チャンネルに接続するようにするのは面倒なため。 case "typingOnChannel": - this.typingOnChannel(body.channel); + await this.typingOnChannel(body.channel); break; case "typingOnMessaging": - this.typingOnMessaging(body); + await this.typingOnMessaging(body); break; } } @@ -513,26 +513,30 @@ export default class Connection { } } - private typingOnChannel(channelId: ChannelModel["id"]) { + private async typingOnChannel(channelId: ChannelModel["id"]) { if (this.user) { - publishToChannelStream(channelId, this.user.id); + await publishToChannelStream(channelId, this.user.id); } } - private typingOnMessaging(param: { + private async typingOnMessaging(param: { partner?: User["id"]; group?: UserGroup["id"]; }) { if (this.user) { if (param.partner) { - publishToChatStream( + await publishToChatStream( param.partner, this.user.id, ChatEvent.Typing, this.user.id, ); } else if (param.group != null) { - publishToGroupChatStream(param.group, ChatEvent.Typing, this.user.id); + await publishToGroupChatStream( + param.group, + ChatEvent.Typing, + this.user.id, + ); } } } diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index e32b4ba13e..51f94842ea 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -88,21 +88,20 @@ serverAdapter.setBasePath(bullBoardPath); app.use(serverAdapter.registerPlugin()); //#endregion +const clientEntry = JSON.parse( + readFileSync( + `${_dirname}/../../../../../built/_client_dist_/manifest.json`, + "utf-8", + ), +)["src/init.ts"]; + // Init renderer app.use( views(`${_dirname}/views`, { extension: "pug", options: { version: config.version, - getClientEntry: () => - process.env.NODE_ENV === "production" - ? config.clientEntry - : JSON.parse( - readFileSync( - `${_dirname}/../../../../../built/_client_dist_/manifest.json`, - "utf-8", - ), - )["src/init.ts"], + clientEntry, config, }, }), diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index 738d3ffc01..100e921377 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -1,8 +1,5 @@ block vars -block loadClientEntry - - const clientEntry = getClientEntry(); - doctype html // diff --git a/packages/backend/src/services/create-notification.ts b/packages/backend/src/services/create-notification.ts index e62bd38ec4..335ba561e7 100644 --- a/packages/backend/src/services/create-notification.ts +++ b/packages/backend/src/services/create-notification.ts @@ -85,7 +85,11 @@ export async function createNotification( if (fresh == null) return; // 既に削除されているかもしれない // We execute this before, because the server side "read" check doesnt work well with push notifications, the app and service worker will decide themself // when it is best to show push notifications - sendPushNotification(notifieeId, PushNotificationKind.Generic, packed); + await sendPushNotification( + notifieeId, + PushNotificationKind.Generic, + packed, + ); if (fresh.isRead) return; //#region ただしミュートしているユーザーからの通知なら無視 diff --git a/packages/backend/src/services/messages/create.ts b/packages/backend/src/services/messages/create.ts index 931a1e4c57..709e9bc235 100644 --- a/packages/backend/src/services/messages/create.ts +++ b/packages/backend/src/services/messages/create.ts @@ -55,46 +55,54 @@ export async function createMessage( if (recipientUser) { if (Users.isLocalUser(user)) { - // 自分のストリーム - publishToChatStream( - message.userId, - recipientUser.id, - ChatEvent.Message, - messageObj, - ); - publishToChatIndexStream( - message.userId, - ChatIndexEvent.Message, - messageObj, - ); + // my stream + await Promise.all([ + publishToChatStream( + message.userId, + recipientUser.id, + ChatEvent.Message, + messageObj, + ), + publishToChatIndexStream( + message.userId, + ChatIndexEvent.Message, + messageObj, + ), + ]); publishMainStream(message.userId, "messagingMessage", messageObj); } if (Users.isLocalUser(recipientUser)) { - // 相手のストリーム - publishToChatStream( - recipientUser.id, - message.userId, - ChatEvent.Message, - messageObj, - ); - publishToChatIndexStream( - recipientUser.id, - ChatIndexEvent.Message, - messageObj, - ); + // recipient's stream + await Promise.all([ + publishToChatStream( + recipientUser.id, + message.userId, + ChatEvent.Message, + messageObj, + ), + publishToChatIndexStream( + recipientUser.id, + ChatIndexEvent.Message, + messageObj, + ), + ]); publishMainStream(recipientUser.id, "messagingMessage", messageObj); } } else if (recipientGroup != null) { // group's stream - publishToGroupChatStream(recipientGroup.id, ChatEvent.Message, messageObj); + await publishToGroupChatStream( + recipientGroup.id, + ChatEvent.Message, + messageObj, + ); // member's stream const joinings = await UserGroupJoinings.findBy({ userGroupId: recipientGroup.id, }); - for (const joining of joinings) { - publishToChatIndexStream( + for await (const joining of joinings) { + await publishToChatIndexStream( joining.userId, ChatIndexEvent.Message, messageObj, @@ -119,7 +127,7 @@ export async function createMessage( //#endregion publishMainStream(recipientUser.id, "unreadMessagingMessage", messageObj); - sendPushNotification( + await sendPushNotification( recipientUser.id, PushNotificationKind.Chat, messageObj, @@ -129,10 +137,10 @@ export async function createMessage( userGroupId: recipientGroup.id, userId: Not(user.id), }); - for (const joining of joinings) { + for await (const joining of joinings) { if (freshMessage.reads.includes(joining.userId)) return; // 既読 publishMainStream(joining.userId, "unreadMessagingMessage", messageObj); - sendPushNotification( + await sendPushNotification( joining.userId, PushNotificationKind.Chat, messageObj, diff --git a/packages/backend/src/services/messages/delete.ts b/packages/backend/src/services/messages/delete.ts index 5fc4c812e2..063e694ad5 100644 --- a/packages/backend/src/services/messages/delete.ts +++ b/packages/backend/src/services/messages/delete.ts @@ -18,18 +18,20 @@ export async function deleteMessage(message: MessagingMessage) { async function postDeleteMessage(message: MessagingMessage) { if (message.recipientId) { - const user = await Users.findOneByOrFail({ id: message.userId }); - const recipient = await Users.findOneByOrFail({ id: message.recipientId }); + const [user, recipient] = await Promise.all([ + Users.findOneByOrFail({ id: message.userId }), + Users.findOneByOrFail({ id: message.recipientId }), + ]); if (Users.isLocalUser(user)) - publishToChatStream( + await publishToChatStream( message.userId, message.recipientId, ChatEvent.Deleted, message.id, ); if (Users.isLocalUser(recipient)) - publishToChatStream( + await publishToChatStream( message.recipientId, message.userId, ChatEvent.Deleted, @@ -46,6 +48,10 @@ async function postDeleteMessage(message: MessagingMessage) { deliver(user, activity, recipient.inbox); } } else if (message.groupId != null) { - publishToGroupChatStream(message.groupId, ChatEvent.Deleted, message.id); + await publishToGroupChatStream( + message.groupId, + ChatEvent.Deleted, + message.id, + ); } } diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index cada20a226..8fe44a60ba 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -42,19 +42,18 @@ import type { IPoll } from "@/models/entities/poll.js"; import { Poll } from "@/models/entities/poll.js"; import { createNotification } from "@/services/create-notification.js"; import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js"; -import { checkHitAntenna } from "@/misc/check-hit-antenna.js"; import { - addNoteToAntenna, + updateAntennasOnNewNote, checkWordMute, genId, genIdAt, + isQuote, isSilencedServer, } from "backend-rs"; import { countSameRenotes } from "@/misc/count-same-renotes.js"; import { deliverToRelays, getCachedRelays } from "../relay.js"; import type { Channel } from "@/models/entities/channel.js"; import { normalizeForSearch } from "@/misc/normalize-for-search.js"; -import { getAntennas } from "@/misc/antenna-cache.js"; import { endedPollNotificationQueue } from "@/queue/queues.js"; import { webhookDeliver } from "@/queue/index.js"; import { Cache } from "@/misc/cache.js"; @@ -374,8 +373,10 @@ export default async ( // Increment notes count (user) incNotesCountOfUser(user); - // Word mute - hardMutesCache + // Word mutes & antenna + const thisNoteIsMutedBy: string[] = []; + + await hardMutesCache .fetch(null, () => UserProfiles.find({ where: { @@ -384,12 +385,13 @@ export default async ( select: ["userId", "mutedWords", "mutedPatterns"], }), ) - .then((us) => { + .then(async (us) => { for (const u of us) { if (u.userId === user.id) return; - checkWordMute(note, u.mutedWords, u.mutedPatterns).then( + await checkWordMute(note, u.mutedWords, u.mutedPatterns).then( (shouldMute: boolean) => { if (shouldMute) { + thisNoteIsMutedBy.push(u.userId); MutedNotes.insert({ id: genId(), userId: u.userId, @@ -402,14 +404,10 @@ export default async ( } }); - // Antenna - for (const antenna of await getAntennas()) { - checkHitAntenna(antenna, note, user).then((hit) => { - if (hit) { - // TODO: do this more sanely - addNoteToAntenna(antenna.id, toRustObject(note)); - } - }); + // type errors will be resolved by https://github.com/napi-rs/napi-rs/pull/2054 + const _note = toRustObject(note); + if (note.renoteId == null || isQuote(_note)) { + await updateAntennasOnNewNote(_note, user, thisNoteIsMutedBy); } // Channel diff --git a/packages/client/package.json b/packages/client/package.json index bdb19950f7..5e086535a4 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -38,7 +38,7 @@ "autobind-decorator": "2.4.0", "autosize": "6.0.1", "broadcast-channel": "7.0.0", - "chart.js": "4.4.2", + "chart.js": "4.4.3", "chartjs-adapter-date-fns": "3.0.0", "chartjs-chart-matrix": "2.0.1", "chartjs-plugin-gradient": "0.6.1", @@ -72,7 +72,7 @@ "qrcode-vue3": "1.6.8", "rollup": "4.17.2", "s-age": "1.1.2", - "sass": "1.77.1", + "sass": "1.77.2", "seedrandom": "3.0.5", "stringz": "2.1.0", "swiper": "11.1.3", @@ -90,6 +90,6 @@ "vue-draggable-plus": "0.4.1", "vue-plyr": "7.0.0", "vue-prism-editor": "2.0.0-alpha.2", - "vue-tsc": "2.0.18" + "vue-tsc": "2.0.19" } } diff --git a/packages/client/src/components/MkNotificationFolded.vue b/packages/client/src/components/MkNotificationFolded.vue index a6678c9f71..c7a2730cd3 100644 --- a/packages/client/src/components/MkNotificationFolded.vue +++ b/packages/client/src/components/MkNotificationFolded.vue @@ -12,6 +12,10 @@ v-if="notification.type === 'renote'" :class="icon('ph-rocket-launch', false)" ></i> + <i + v-if="notification.type === 'pollVote'" + :class="icon('ph-microphone-stage', false)" + ></i> <XReactionIcon v-else-if=" showEmojiReactions && notification.type === 'reaction' @@ -38,6 +42,7 @@ <span class="avatars"> <MkAvatar v-for="user in users" + :key="user.id" class="avatar" :user="user" /> @@ -141,6 +146,8 @@ function getText() { case "reaction": res = i18n.ts._notification.reacted; break; + case "pollVote": + res = i18n.ts._notification.voted; } if (userleft.value > 0) { res = i18n.t("_notification.andCountUsers", { diff --git a/packages/client/src/scripts/fold.ts b/packages/client/src/scripts/fold.ts index f693b7526b..fe98ecb50f 100644 --- a/packages/client/src/scripts/fold.ts +++ b/packages/client/src/scripts/fold.ts @@ -68,6 +68,8 @@ export function foldNotifications(ns: entities.Notification[]) { return `renote-${n.note.renote.id}`; case "reaction": return `reaction-${n.reaction}-of-${n.note.id}`; + case "pollVote": + return `pollVote-${n.note.id}`; default: { return `${n.id}`; } @@ -78,7 +80,11 @@ export function foldNotifications(ns: entities.Notification[]) { function check( ns: entities.Notification[], ): ns is FoldableNotification[] { - return represent.type === "renote" || represent.type === "reaction"; + return ( + represent.type === "renote" || + represent.type === "reaction" || + represent.type === "pollVote" + ); } if (!check(ns)) { return represent; diff --git a/packages/client/src/types/notification.ts b/packages/client/src/types/notification.ts index 0b7090ae6b..8501c4c625 100644 --- a/packages/client/src/types/notification.ts +++ b/packages/client/src/types/notification.ts @@ -2,7 +2,8 @@ import type { entities } from "firefish-js"; export type FoldableNotification = | entities.RenoteNotification - | entities.ReactionNotification; + | entities.ReactionNotification + | entities.PollVoteNotification; interface Fold<T extends FoldableNotification> { id: string; @@ -21,11 +22,16 @@ export type ReactionNotificationFolded = Fold<entities.ReactionNotification> & { reaction: string; }; +export type PollVotedNotificationFolded = Fold<entities.PollVoteNotification>; + export type GetNotificationFoldedType<T extends FoldableNotification> = T["type"] extends "renote" ? RenoteNotificationFolded - : ReactionNotificationFolded; + : T["type"] extends "reaction" + ? ReactionNotificationFolded + : PollVotedNotificationFolded; export type NotificationFolded = | RenoteNotificationFolded - | ReactionNotificationFolded; + | ReactionNotificationFolded + | PollVotedNotificationFolded; diff --git a/packages/client/vite.config.ts b/packages/client/vite.config.ts index 14bbc44661..aa9d1a9186 100644 --- a/packages/client/vite.config.ts +++ b/packages/client/vite.config.ts @@ -1,4 +1,3 @@ -import * as fs from "fs"; import pluginVue from "@vitejs/plugin-vue"; import { defineConfig } from "vite"; @@ -23,13 +22,6 @@ const extensions = [ ]; export default defineConfig(({ command, mode }) => { - fs.mkdirSync(`${__dirname}/../../built`, { recursive: true }); - fs.writeFileSync( - `${__dirname}/../../built/meta.json`, - JSON.stringify({ version: meta.version }), - "utf-8", - ); - return { base: "/assets/", diff --git a/packages/macro-rs/Cargo.toml b/packages/macro-rs/Cargo.toml index 25fa8e74bf..c64734709f 100644 --- a/packages/macro-rs/Cargo.toml +++ b/packages/macro-rs/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "macro_rs" +name = "macro-rs" version = "0.0.0" edition = "2021" rust-version = "1.74" @@ -11,6 +11,8 @@ proc-macro = true convert_case = { workspace = true } proc-macro2 = { workspace = true } quote = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } syn = { workspace = true, features = ["full", "extra-traits"] } [dev-dependencies] diff --git a/packages/macro-rs/src/lib.rs b/packages/macro-rs/src/lib.rs index 3481d0cd87..b70bfdbe2e 100644 --- a/packages/macro-rs/src/lib.rs +++ b/packages/macro-rs/src/lib.rs @@ -2,6 +2,21 @@ use convert_case::{Case, Casing}; use proc_macro2::{TokenStream, TokenTree}; use quote::{quote, ToTokens}; +/// Read the version field in the project root package.json at compile time +#[proc_macro] +pub fn read_version_from_package_json(_item: proc_macro::TokenStream) -> proc_macro::TokenStream { + #[derive(serde::Deserialize)] + struct PackageJson { + version: String, + } + + let file = std::fs::File::open("package.json").expect("Failed to open package.json"); + let json: PackageJson = serde_json::from_reader(file).unwrap(); + let version = &json.version; + + quote! { #version }.into() +} + /// Export this function, struct, enum, const, etc. to TypeScript. #[proc_macro_attribute] pub fn export( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56657373e1..a774da6733 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -85,8 +85,8 @@ importers: specifier: 7.0.1 version: 7.0.1 aws-sdk: - specifier: 2.1621.0 - version: 2.1621.0 + specifier: 2.1623.0 + version: 2.1623.0 axios: specifier: 1.6.8 version: 1.6.8 @@ -97,8 +97,8 @@ importers: specifier: 2.0.5 version: 2.0.5 bull: - specifier: 4.12.4 - version: 4.12.4 + specifier: 4.12.6 + version: 4.12.6 cacheable-lookup: specifier: TheEssem/cacheable-lookup version: https://codeload.github.com/TheEssem/cacheable-lookup/tar.gz/dd2fb616366a3c68dcf321a57a67295967b204bf @@ -108,9 +108,6 @@ importers: chalk: specifier: 5.3.0 version: 5.3.0 - chalk-template: - specifier: 1.1.0 - version: 1.1.0 cli-highlight: specifier: 2.1.11 version: 2.1.11 @@ -151,8 +148,8 @@ importers: specifier: 4.0.0 version: 4.0.0 got: - specifier: 14.2.1 - version: 14.2.1 + specifier: 14.3.0 + version: 14.3.0 gunzip-maybe: specifier: 1.4.2 version: 1.4.2 @@ -298,8 +295,8 @@ importers: specifier: 7.6.2 version: 7.6.2 sharp: - specifier: 0.33.3 - version: 0.33.3 + specifier: 0.33.4 + version: 0.33.4 stringz: specifier: 2.1.0 version: 2.1.0 @@ -483,8 +480,8 @@ importers: specifier: 7.0.3 version: 7.0.3 eslint: - specifier: 9.2.0 - version: 9.2.0 + specifier: 9.3.0 + version: 9.3.0 mocha: specifier: 10.4.0 version: 10.4.0 @@ -529,10 +526,10 @@ importers: devDependencies: '@eslint-sets/eslint-config-vue3': specifier: 5.13.0 - version: 5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(eslint@9.2.0)(prettier@2.8.8)(typescript@5.4.5) + version: 5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(eslint@9.3.0)(prettier@2.8.8)(typescript@5.4.5) '@eslint-sets/eslint-config-vue3-ts': specifier: 3.3.0 - version: 3.3.0(@babel/core@7.24.5)(eslint@9.2.0)(prettier@2.8.8)(typescript@5.4.5) + version: 3.3.0(@babel/core@7.24.5)(eslint@9.3.0)(prettier@2.8.8)(typescript@5.4.5) '@misskey-dev/browser-image-resizer': specifier: 2024.1.0 version: 2024.1.0 @@ -592,7 +589,7 @@ importers: version: 9.0.8 '@vitejs/plugin-vue': specifier: 5.0.4 - version: 5.0.4(vite@5.2.11(@types/node@20.12.12)(sass@1.77.1)(stylus@0.57.0)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5)) + version: 5.0.4(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(stylus@0.57.0)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5)) '@vue/runtime-core': specifier: 3.4.27 version: 3.4.27 @@ -606,20 +603,20 @@ importers: specifier: 7.0.0 version: 7.0.0 chart.js: - specifier: 4.4.2 - version: 4.4.2 + specifier: 4.4.3 + version: 4.4.3 chartjs-adapter-date-fns: specifier: 3.0.0 - version: 3.0.0(chart.js@4.4.2)(date-fns@3.6.0) + version: 3.0.0(chart.js@4.4.3)(date-fns@3.6.0) chartjs-chart-matrix: specifier: 2.0.1 - version: 2.0.1(chart.js@4.4.2) + version: 2.0.1(chart.js@4.4.3) chartjs-plugin-gradient: specifier: 0.6.1 - version: 0.6.1(chart.js@4.4.2) + version: 0.6.1(chart.js@4.4.3) chartjs-plugin-zoom: specifier: 2.0.1 - version: 2.0.1(chart.js@4.4.2) + version: 2.0.1(chart.js@4.4.3) city-timezones: specifier: 1.2.1 version: 1.2.1 @@ -637,7 +634,7 @@ importers: version: 3.0.12 eslint-plugin-file-progress: specifier: 1.4.0 - version: 1.4.0(eslint@9.2.0) + version: 1.4.0(eslint@9.3.0) eventemitter3: specifier: 5.0.1 version: 5.0.1 @@ -708,8 +705,8 @@ importers: specifier: 1.1.2 version: 1.1.2 sass: - specifier: 1.77.1 - version: 1.77.1 + specifier: 1.77.2 + version: 1.77.2 seedrandom: specifier: 3.0.5 version: 3.0.5 @@ -745,10 +742,10 @@ importers: version: 9.0.1 vite: specifier: 5.2.11 - version: 5.2.11(@types/node@20.12.12)(sass@1.77.1)(stylus@0.57.0)(terser@5.31.0) + version: 5.2.11(@types/node@20.12.12)(sass@1.77.2)(stylus@0.57.0)(terser@5.31.0) vite-plugin-compression: specifier: 0.5.1 - version: 0.5.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.1)(stylus@0.57.0)(terser@5.31.0)) + version: 0.5.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(stylus@0.57.0)(terser@5.31.0)) vue: specifier: 3.4.27 version: 3.4.27(typescript@5.4.5) @@ -762,8 +759,8 @@ importers: specifier: 2.0.0-alpha.2 version: 2.0.0-alpha.2(vue@3.4.27(typescript@5.4.5)) vue-tsc: - specifier: 2.0.18 - version: 2.0.18(typescript@5.4.5) + specifier: 2.0.19 + version: 2.0.19(typescript@5.4.5) packages/firefish-js: dependencies: @@ -937,10 +934,10 @@ importers: version: 6.2.1 vite: specifier: 5.2.11 - version: 5.2.11(@types/node@20.12.12)(sass@1.77.1)(stylus@0.57.0)(terser@5.31.0) + version: 5.2.11(@types/node@20.12.12)(sass@1.77.2)(stylus@0.57.0)(terser@5.31.0) vite-plugin-compression: specifier: 0.5.1 - version: 0.5.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.1)(stylus@0.57.0)(terser@5.31.0)) + version: 0.5.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(stylus@0.57.0)(terser@5.31.0)) packages: @@ -1494,16 +1491,16 @@ packages: resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/eslintrc@3.0.2': - resolution: {integrity: sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg==} + '@eslint/eslintrc@3.1.0': + resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/js@8.57.0': resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/js@9.2.0': - resolution: {integrity: sha512-ESiIudvhoYni+MdsI8oD7skpprZ89qKocwRM2KEvhhBJ9nl5MRh7BXU5GTod7Mdygq+AUl+QzId6iWJKR/wABA==} + '@eslint/js@9.3.0': + resolution: {integrity: sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@fastify/busboy@2.1.1': @@ -1525,18 +1522,18 @@ packages: '@humanwhocodes/object-schema@2.0.3': resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - '@humanwhocodes/retry@0.2.4': - resolution: {integrity: sha512-Ttl/jHpxfS3st5sxwICYfk4pOH0WrLI1SpW283GgQL7sCWU7EHIOhX4b4fkIxr3tkfzwg8+FNojtzsIEE7Ecgg==} + '@humanwhocodes/retry@0.3.0': + resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} engines: {node: '>=18.18'} - '@img/sharp-darwin-arm64@0.33.3': - resolution: {integrity: sha512-FaNiGX1MrOuJ3hxuNzWgsT/mg5OHG/Izh59WW2mk1UwYHUwtfbhk5QNKYZgxf0pLOhx9ctGiGa2OykD71vOnSw==} + '@img/sharp-darwin-arm64@0.33.4': + resolution: {integrity: sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.33.3': - resolution: {integrity: sha512-2QeSl7QDK9ru//YBT4sQkoq7L0EAJZA3rtV+v9p8xTKl4U1bUqTIaCnoC7Ctx2kCjQgwFXDasOtPTCT8eCTXvw==} + '@img/sharp-darwin-x64@0.33.4': + resolution: {integrity: sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [darwin] @@ -1589,55 +1586,55 @@ packages: cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.33.3': - resolution: {integrity: sha512-Zf+sF1jHZJKA6Gor9hoYG2ljr4wo9cY4twaxgFDvlG0Xz9V7sinsPp8pFd1XtlhTzYo0IhDbl3rK7P6MzHpnYA==} + '@img/sharp-linux-arm64@0.33.4': + resolution: {integrity: sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.33.3': - resolution: {integrity: sha512-Q7Ee3fFSC9P7vUSqVEF0zccJsZ8GiiCJYGWDdhEjdlOeS9/jdkyJ6sUSPj+bL8VuOYFSbofrW0t/86ceVhx32w==} + '@img/sharp-linux-arm@0.33.4': + resolution: {integrity: sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==} engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm] os: [linux] - '@img/sharp-linux-s390x@0.33.3': - resolution: {integrity: sha512-vFk441DKRFepjhTEH20oBlFrHcLjPfI8B0pMIxGm3+yilKyYeHEVvrZhYFdqIseSclIqbQ3SnZMwEMWonY5XFA==} - engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-linux-s390x@0.33.4': + resolution: {integrity: sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==} + engines: {glibc: '>=2.31', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.33.3': - resolution: {integrity: sha512-Q4I++herIJxJi+qmbySd072oDPRkCg/SClLEIDh5IL9h1zjhqjv82H0Seupd+q2m0yOfD+/fJnjSoDFtKiHu2g==} + '@img/sharp-linux-x64@0.33.4': + resolution: {integrity: sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.33.3': - resolution: {integrity: sha512-qnDccehRDXadhM9PM5hLvcPRYqyFCBN31kq+ErBSZtZlsAc1U4Z85xf/RXv1qolkdu+ibw64fUDaRdktxTNP9A==} + '@img/sharp-linuxmusl-arm64@0.33.4': + resolution: {integrity: sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==} engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.33.3': - resolution: {integrity: sha512-Jhchim8kHWIU/GZ+9poHMWRcefeaxFIs9EBqf9KtcC14Ojk6qua7ghKiPs0sbeLbLj/2IGBtDcxHyjCdYWkk2w==} + '@img/sharp-linuxmusl-x64@0.33.4': + resolution: {integrity: sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==} engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.33.3': - resolution: {integrity: sha512-68zivsdJ0koE96stdUfM+gmyaK/NcoSZK5dV5CAjES0FUXS9lchYt8LAB5rTbM7nlWtxaU/2GON0HVN6/ZYJAQ==} + '@img/sharp-wasm32@0.33.4': + resolution: {integrity: sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [wasm32] - '@img/sharp-win32-ia32@0.33.3': - resolution: {integrity: sha512-CyimAduT2whQD8ER4Ux7exKrtfoaUiVr7HG0zZvO0XTFn2idUWljjxv58GxNTkFb8/J9Ub9AqITGkJD6ZginxQ==} + '@img/sharp-win32-ia32@0.33.4': + resolution: {integrity: sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.33.3': - resolution: {integrity: sha512-viT4fUIDKnli3IfOephGnolMzhz5VaTvDRkYqtZxOMIoMQ4MrAziO7pT1nVnOt2FAm7qW5aa+CCc13aEY6Le0g==} + '@img/sharp-win32-x64@0.33.4': + resolution: {integrity: sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [win32] @@ -2131,8 +2128,8 @@ packages: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} - '@sindresorhus/is@6.3.0': - resolution: {integrity: sha512-bOSPck7aIJjASXIg1qvXSIjXhVBpIEKdl2Wxg4pVqoTRPL8wWExKBrnGIh6CEnhkFQHfc36k7APhO3uXV4g5xg==} + '@sindresorhus/is@6.3.1': + resolution: {integrity: sha512-FX4MfcifwJyFOI2lPoX7PQxCqx8BG1HCho7WdiXwpEQx1Ycij0JxkfYtGK7yqNScrZGSlt6RE6sw8QYoH7eKnQ==} engines: {node: '>=16'} '@sindresorhus/merge-streams@4.0.0': @@ -2752,8 +2749,8 @@ packages: '@vue/compiler-ssr@3.4.27': resolution: {integrity: sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==} - '@vue/language-core@2.0.18': - resolution: {integrity: sha512-MwKRQAReHN1z7P3/8k/ISC5MjDRjHxGyitn50jWrMmzW9FNySG/1NxMPgAHcVJ4zApJUolS9TexYzT4I6BKL5w==} + '@vue/language-core@2.0.19': + resolution: {integrity: sha512-A9EGOnvb51jOvnCYoRLnMP+CcoPlbZVxI9gZXE/y2GksRWM6j/PrLEIC++pnosWTN08tFpJgxhSS//E9v/Sg+Q==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -3030,8 +3027,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - aws-sdk@2.1621.0: - resolution: {integrity: sha512-v6rxF1u0GpQG1Y9Wul9iaqulSV2uEnp0kHKd6/lZcvEgTYhtJ8N0hOLfqRSWHjH5PaIa46hR9zSAp51r8DJ/OA==} + aws-sdk@2.1623.0: + resolution: {integrity: sha512-SFPc+QJqoghsE0nn6YSmrDDDPpWc3m4rcDQYg6W3GQek+f1v6kycxM5+N58pMZ2iWhRSOTf9NQRcZj0ZU3PklQ==} engines: {node: '>= 10.0.0'} axios@0.24.0: @@ -3191,8 +3188,8 @@ packages: builtins@5.1.0: resolution: {integrity: sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==} - bull@4.12.4: - resolution: {integrity: sha512-a+xWS52Mmc9L7S0zYLEPMUwx/r5IkQyMKKKGVKkUurl3zXeg1ktPZjp9NvdaFY/40XtD/RP80KURjjkjAQnF0g==} + bull@4.12.6: + resolution: {integrity: sha512-QViK1i938moggAtlwhmJ8RmLadswgWKlqOFX9w+WBIAuV2L8qrte1XOIs7KUrRc9fbIfJTziaq7NZEpuaMO4Lw==} engines: {node: '>=12'} busboy@1.6.0: @@ -3220,9 +3217,9 @@ packages: version: 7.0.0 engines: {node: '>=14.16'} - cacheable-request@10.2.14: - resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} - engines: {node: '>=14.16'} + cacheable-request@12.0.1: + resolution: {integrity: sha512-Yo9wGIQUaAfIbk+qY0X4cDQgCosecfBe3V9NSyeY4qPC2SAkbCS4Xj79VP8WOzitpJUZKc/wsRCYF5ariDIwkg==} + engines: {node: '>=18'} cacheable-request@7.0.4: resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} @@ -3248,8 +3245,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001618: - resolution: {integrity: sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg==} + caniuse-lite@1.0.30001620: + resolution: {integrity: sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==} canonicalize@1.0.8: resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==} @@ -3261,10 +3258,6 @@ packages: cbor-x@1.5.9: resolution: {integrity: sha512-OEI5rEu3MeR0WWNUXuIGkxmbXVhABP+VtgAXzm48c9ulkrsvxshjjk94XSOGphyAKeNGLPfAxxzEtgQ6rEVpYQ==} - chalk-template@1.1.0: - resolution: {integrity: sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==} - engines: {node: '>=14.16'} - chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -3293,8 +3286,8 @@ packages: character-reference-invalid@1.1.4: resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} - chart.js@4.4.2: - resolution: {integrity: sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==} + chart.js@4.4.3: + resolution: {integrity: sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==} engines: {pnpm: '>=8'} chartjs-adapter-date-fns@3.0.0: @@ -4002,8 +3995,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.4.772: - resolution: {integrity: sha512-jFfEbxR/abTTJA3ci+2ok1NTuOBBtB4jH+UT6PUmRN+DY3WSD4FFRsgoVQ+QNIJ0T7wrXwzsWCI2WKC46b++2A==} + electron-to-chromium@1.4.774: + resolution: {integrity: sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg==} emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -4057,8 +4050,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@1.5.2: - resolution: {integrity: sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==} + es-module-lexer@1.5.3: + resolution: {integrity: sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==} es-object-atoms@1.0.0: resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} @@ -4375,8 +4368,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true - eslint@9.2.0: - resolution: {integrity: sha512-0n/I88vZpCOzO+PQpt0lbsqmn9AsnsJAQseIqhZFI8ibQT0U1AkEKRxA3EVMos0BoHSXDQvCXY25TUjB5tr8Og==} + eslint@9.3.0: + resolution: {integrity: sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true @@ -4797,8 +4790,8 @@ packages: resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} engines: {node: '>=10.19.0'} - got@14.2.1: - resolution: {integrity: sha512-KOaPMremmsvx6l9BLC04LYE6ZFW4x7e4HkTe3LwBmtuYYQwpeS4XKqzhubTIkaQ1Nr+eXxeori0zuwupXMovBQ==} + got@14.3.0: + resolution: {integrity: sha512-vZkrXdq5BtPWTXqvjXSpl6zky3zpHaOVfSug/RfFHu3YrtSsvYzopVMDqrh2do77WnGoCSSRCHW25zXOSAQ9zw==} engines: {node: '>=20'} graceful-fs@4.2.11: @@ -6880,8 +6873,8 @@ packages: sanitize-html@2.13.0: resolution: {integrity: sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==} - sass@1.77.1: - resolution: {integrity: sha512-OMEyfirt9XEfyvocduUIOlUSkWOXS/LAt6oblR/ISXCTukyavjex+zQNm51pPCOiFKY1QpWvEH1EeCkgyV3I6w==} + sass@1.77.2: + resolution: {integrity: sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==} engines: {node: '>=14.0.0'} hasBin: true @@ -6963,8 +6956,8 @@ packages: resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} hasBin: true - sharp@0.33.3: - resolution: {integrity: sha512-vHUeXJU1UvlO/BNwTpT0x/r53WkLUVxrmb5JTgW92fdFCFk0ispLMAeu/jPO2vjkXM1fYUi3K7/qcLF47pwM1A==} + sharp@0.33.4: + resolution: {integrity: sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==} engines: {libvips: '>=8.15.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@1.2.0: @@ -7766,8 +7759,8 @@ packages: vue-template-compiler@2.7.16: resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} - vue-tsc@2.0.18: - resolution: {integrity: sha512-0SrsAJzsUrX7A6aXLsBrXrAesozAirASPnVz5VUt2+4imFNP2cEXtLQy1s8ayUHLex3zoYIoZVgZ7h7UgqaEVw==} + vue-tsc@2.0.19: + resolution: {integrity: sha512-JWay5Zt2/871iodGF72cELIbcAoPyhJxq56mPPh+M2K7IwI688FMrFKc/+DvB05wDWEuCPexQJ6L10zSwzzapg==} hasBin: true peerDependencies: typescript: '*' @@ -8079,11 +8072,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/eslint-parser@7.24.5(@babel/core@7.24.5)(eslint@9.2.0)': + '@babel/eslint-parser@7.24.5(@babel/core@7.24.5)(eslint@9.3.0)': dependencies: '@babel/core': 7.24.5 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 - eslint: 9.2.0 + eslint: 9.3.0 eslint-visitor-keys: 2.1.0 semver: 6.3.1 @@ -8581,32 +8574,32 @@ snapshots: eslint: 8.57.0 eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.4.0(eslint@9.2.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.3.0)': dependencies: - eslint: 9.2.0 + eslint: 9.3.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.10.0': {} - '@eslint-sets/eslint-config-basic@3.3.0(@babel/core@7.24.5)(@typescript-eslint/parser@5.62.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(prettier@2.8.8)': + '@eslint-sets/eslint-config-basic@3.3.0(@babel/core@7.24.5)(@typescript-eslint/parser@5.62.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(prettier@2.8.8)': dependencies: - '@babel/eslint-parser': 7.24.5(@babel/core@7.24.5)(eslint@9.2.0) - eslint: 9.2.0 - eslint-config-prettier: 8.10.0(eslint@9.2.0) - eslint-plugin-eslint-comments: 3.2.0(eslint@9.2.0) + '@babel/eslint-parser': 7.24.5(@babel/core@7.24.5)(eslint@9.3.0) + eslint: 9.3.0 + eslint-config-prettier: 8.10.0(eslint@9.3.0) + eslint-plugin-eslint-comments: 3.2.0(eslint@9.3.0) eslint-plugin-html: 7.1.0 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0) - eslint-plugin-jsonc: 2.15.1(eslint@9.2.0) - eslint-plugin-markdown: 3.0.1(eslint@9.2.0) - eslint-plugin-n: 15.7.0(eslint@9.2.0) - eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.10.0(eslint@9.2.0))(eslint@9.2.0)(prettier@2.8.8) - eslint-plugin-promise: 5.2.0(eslint@9.2.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0) + eslint-plugin-jsonc: 2.15.1(eslint@9.3.0) + eslint-plugin-markdown: 3.0.1(eslint@9.3.0) + eslint-plugin-n: 15.7.0(eslint@9.3.0) + eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.10.0(eslint@9.3.0))(eslint@9.3.0)(prettier@2.8.8) + eslint-plugin-promise: 5.2.0(eslint@9.3.0) eslint-plugin-tsdoc: 0.2.17 - eslint-plugin-unicorn: 45.0.2(eslint@9.2.0) - eslint-plugin-yml: 1.14.0(eslint@9.2.0) + eslint-plugin-unicorn: 45.0.2(eslint@9.3.0) + eslint-plugin-yml: 1.14.0(eslint@9.3.0) jsonc-eslint-parser: 2.4.0 prettier: 2.8.8 - vue-eslint-parser: 9.4.2(eslint@9.2.0) + vue-eslint-parser: 9.4.2(eslint@9.3.0) yaml-eslint-parser: 1.2.2 transitivePeerDependencies: - '@babel/core' @@ -8615,25 +8608,25 @@ snapshots: - eslint-import-resolver-webpack - supports-color - '@eslint-sets/eslint-config-basic@5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(@typescript-eslint/parser@6.21.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(prettier@2.8.8)(typescript@5.4.5)': + '@eslint-sets/eslint-config-basic@5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(@typescript-eslint/parser@6.21.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(prettier@2.8.8)(typescript@5.4.5)': dependencies: - '@babel/eslint-parser': 7.24.5(@babel/core@7.24.5)(eslint@9.2.0) - eslint: 9.2.0 - eslint-config-prettier: 9.1.0(eslint@9.2.0) - eslint-plugin-eslint-comments: 3.2.0(eslint@9.2.0) + '@babel/eslint-parser': 7.24.5(@babel/core@7.24.5)(eslint@9.3.0) + eslint: 9.3.0 + eslint-config-prettier: 9.1.0(eslint@9.3.0) + eslint-plugin-eslint-comments: 3.2.0(eslint@9.3.0) eslint-plugin-html: 7.1.0 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0) - eslint-plugin-jsonc: 2.15.1(eslint@9.2.0) - eslint-plugin-markdown: 3.0.1(eslint@9.2.0) - eslint-plugin-n: 16.6.2(eslint@9.2.0) - eslint-plugin-prettier: 5.1.3(@types/eslint@8.56.10)(eslint-config-prettier@9.1.0(eslint@9.2.0))(eslint@9.2.0)(prettier@2.8.8) - eslint-plugin-promise: 6.1.1(eslint@9.2.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0) + eslint-plugin-jsonc: 2.15.1(eslint@9.3.0) + eslint-plugin-markdown: 3.0.1(eslint@9.3.0) + eslint-plugin-n: 16.6.2(eslint@9.3.0) + eslint-plugin-prettier: 5.1.3(@types/eslint@8.56.10)(eslint-config-prettier@9.1.0(eslint@9.3.0))(eslint@9.3.0)(prettier@2.8.8) + eslint-plugin-promise: 6.1.1(eslint@9.3.0) eslint-plugin-tsdoc: 0.2.17 - eslint-plugin-unicorn: 40.1.0(eslint@9.2.0) - eslint-plugin-yml: 1.14.0(eslint@9.2.0) + eslint-plugin-unicorn: 40.1.0(eslint@9.3.0) + eslint-plugin-yml: 1.14.0(eslint@9.3.0) jsonc-eslint-parser: 2.4.0 prettier: 2.8.8 - vue-eslint-parser: 9.4.2(eslint@9.2.0) + vue-eslint-parser: 9.4.2(eslint@9.3.0) yaml-eslint-parser: 1.2.2 optionalDependencies: typescript: 5.4.5 @@ -8645,14 +8638,14 @@ snapshots: - eslint-import-resolver-webpack - supports-color - '@eslint-sets/eslint-config-ts@3.3.0(@babel/core@7.24.5)(eslint@9.2.0)(prettier@2.8.8)(typescript@5.4.5)': + '@eslint-sets/eslint-config-ts@3.3.0(@babel/core@7.24.5)(eslint@9.3.0)(prettier@2.8.8)(typescript@5.4.5)': dependencies: - '@eslint-sets/eslint-config-basic': 3.3.0(@babel/core@7.24.5)(@typescript-eslint/parser@5.62.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(prettier@2.8.8) - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5) - '@typescript-eslint/parser': 5.62.0(eslint@9.2.0)(typescript@5.4.5) - eslint: 9.2.0 - eslint-config-prettier: 8.10.0(eslint@9.2.0) - eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.10.0(eslint@9.2.0))(eslint@9.2.0)(prettier@2.8.8) + '@eslint-sets/eslint-config-basic': 3.3.0(@babel/core@7.24.5)(@typescript-eslint/parser@5.62.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(prettier@2.8.8) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5) + '@typescript-eslint/parser': 5.62.0(eslint@9.3.0)(typescript@5.4.5) + eslint: 9.3.0 + eslint-config-prettier: 8.10.0(eslint@9.3.0) + eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.10.0(eslint@9.3.0))(eslint@9.3.0)(prettier@2.8.8) eslint-plugin-tsdoc: 0.2.17 prettier: 2.8.8 typescript: 5.4.5 @@ -8662,14 +8655,14 @@ snapshots: - eslint-import-resolver-webpack - supports-color - '@eslint-sets/eslint-config-ts@5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(eslint@9.2.0)(prettier@2.8.8)(typescript@5.4.5)': + '@eslint-sets/eslint-config-ts@5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(eslint@9.3.0)(prettier@2.8.8)(typescript@5.4.5)': dependencies: - '@eslint-sets/eslint-config-basic': 5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(@typescript-eslint/parser@6.21.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(prettier@2.8.8)(typescript@5.4.5) - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5) - '@typescript-eslint/parser': 6.21.0(eslint@9.2.0)(typescript@5.4.5) - eslint: 9.2.0 - eslint-config-prettier: 9.1.0(eslint@9.2.0) - eslint-plugin-prettier: 5.1.3(@types/eslint@8.56.10)(eslint-config-prettier@9.1.0(eslint@9.2.0))(eslint@9.2.0)(prettier@2.8.8) + '@eslint-sets/eslint-config-basic': 5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(@typescript-eslint/parser@6.21.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(prettier@2.8.8)(typescript@5.4.5) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5) + '@typescript-eslint/parser': 6.21.0(eslint@9.3.0)(typescript@5.4.5) + eslint: 9.3.0 + eslint-config-prettier: 9.1.0(eslint@9.3.0) + eslint-plugin-prettier: 5.1.3(@types/eslint@8.56.10)(eslint-config-prettier@9.1.0(eslint@9.3.0))(eslint@9.3.0)(prettier@2.8.8) eslint-plugin-tsdoc: 0.2.17 prettier: 2.8.8 optionalDependencies: @@ -8681,44 +8674,44 @@ snapshots: - eslint-import-resolver-webpack - supports-color - '@eslint-sets/eslint-config-vue3-ts@3.3.0(@babel/core@7.24.5)(eslint@9.2.0)(prettier@2.8.8)(typescript@5.4.5)': + '@eslint-sets/eslint-config-vue3-ts@3.3.0(@babel/core@7.24.5)(eslint@9.3.0)(prettier@2.8.8)(typescript@5.4.5)': dependencies: - '@eslint-sets/eslint-config-ts': 3.3.0(@babel/core@7.24.5)(eslint@9.2.0)(prettier@2.8.8)(typescript@5.4.5) - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5) - '@typescript-eslint/parser': 5.62.0(eslint@9.2.0)(typescript@5.4.5) - eslint: 9.2.0 - eslint-config-prettier: 8.10.0(eslint@9.2.0) - eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.10.0(eslint@9.2.0))(eslint@9.2.0)(prettier@2.8.8) + '@eslint-sets/eslint-config-ts': 3.3.0(@babel/core@7.24.5)(eslint@9.3.0)(prettier@2.8.8)(typescript@5.4.5) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5) + '@typescript-eslint/parser': 5.62.0(eslint@9.3.0)(typescript@5.4.5) + eslint: 9.3.0 + eslint-config-prettier: 8.10.0(eslint@9.3.0) + eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.10.0(eslint@9.3.0))(eslint@9.3.0)(prettier@2.8.8) eslint-plugin-tsdoc: 0.2.17 eslint-plugin-vitest-globals: 1.5.0 - eslint-plugin-vue: 9.26.0(eslint@9.2.0) - eslint-plugin-vue-scoped-css: 2.8.0(eslint@9.2.0)(vue-eslint-parser@9.4.2(eslint@9.2.0)) + eslint-plugin-vue: 9.26.0(eslint@9.3.0) + eslint-plugin-vue-scoped-css: 2.8.0(eslint@9.3.0)(vue-eslint-parser@9.4.2(eslint@9.3.0)) prettier: 2.8.8 typescript: 5.4.5 - vue-eslint-parser: 9.4.2(eslint@9.2.0) + vue-eslint-parser: 9.4.2(eslint@9.3.0) transitivePeerDependencies: - '@babel/core' - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - '@eslint-sets/eslint-config-vue3@5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(eslint@9.2.0)(prettier@2.8.8)(typescript@5.4.5)': + '@eslint-sets/eslint-config-vue3@5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(eslint@9.3.0)(prettier@2.8.8)(typescript@5.4.5)': dependencies: - '@eslint-sets/eslint-config-basic': 5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(@typescript-eslint/parser@6.21.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(prettier@2.8.8)(typescript@5.4.5) - '@eslint-sets/eslint-config-ts': 5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(eslint@9.2.0)(prettier@2.8.8)(typescript@5.4.5) - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5) - '@typescript-eslint/parser': 6.21.0(eslint@9.2.0)(typescript@5.4.5) - eslint: 9.2.0 - eslint-config-prettier: 9.1.0(eslint@9.2.0) - eslint-plugin-jsdoc: 48.2.5(eslint@9.2.0) - eslint-plugin-prettier: 5.1.3(@types/eslint@8.56.10)(eslint-config-prettier@9.1.0(eslint@9.2.0))(eslint@9.2.0)(prettier@2.8.8) + '@eslint-sets/eslint-config-basic': 5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(@typescript-eslint/parser@6.21.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(prettier@2.8.8)(typescript@5.4.5) + '@eslint-sets/eslint-config-ts': 5.13.0(@babel/core@7.24.5)(@types/eslint@8.56.10)(eslint@9.3.0)(prettier@2.8.8)(typescript@5.4.5) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5) + '@typescript-eslint/parser': 6.21.0(eslint@9.3.0)(typescript@5.4.5) + eslint: 9.3.0 + eslint-config-prettier: 9.1.0(eslint@9.3.0) + eslint-plugin-jsdoc: 48.2.5(eslint@9.3.0) + eslint-plugin-prettier: 5.1.3(@types/eslint@8.56.10)(eslint-config-prettier@9.1.0(eslint@9.3.0))(eslint@9.3.0)(prettier@2.8.8) eslint-plugin-tsdoc: 0.2.17 eslint-plugin-vitest-globals: 1.5.0 - eslint-plugin-vue: 9.26.0(eslint@9.2.0) - eslint-plugin-vue-scoped-css: 2.8.0(eslint@9.2.0)(vue-eslint-parser@9.4.2(eslint@9.2.0)) + eslint-plugin-vue: 9.26.0(eslint@9.3.0) + eslint-plugin-vue-scoped-css: 2.8.0(eslint@9.3.0)(vue-eslint-parser@9.4.2(eslint@9.3.0)) local-pkg: 0.5.0 prettier: 2.8.8 - vue-eslint-parser: 9.4.2(eslint@9.2.0) + vue-eslint-parser: 9.4.2(eslint@9.3.0) optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: @@ -8742,7 +8735,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/eslintrc@3.0.2': + '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 debug: 4.3.4(supports-color@8.1.1) @@ -8758,7 +8751,7 @@ snapshots: '@eslint/js@8.57.0': {} - '@eslint/js@9.2.0': {} + '@eslint/js@9.3.0': {} '@fastify/busboy@2.1.1': {} @@ -8782,14 +8775,14 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} - '@humanwhocodes/retry@0.2.4': {} + '@humanwhocodes/retry@0.3.0': {} - '@img/sharp-darwin-arm64@0.33.3': + '@img/sharp-darwin-arm64@0.33.4': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.0.2 optional: true - '@img/sharp-darwin-x64@0.33.3': + '@img/sharp-darwin-x64@0.33.4': optionalDependencies: '@img/sharp-libvips-darwin-x64': 1.0.2 optional: true @@ -8818,45 +8811,45 @@ snapshots: '@img/sharp-libvips-linuxmusl-x64@1.0.2': optional: true - '@img/sharp-linux-arm64@0.33.3': + '@img/sharp-linux-arm64@0.33.4': optionalDependencies: '@img/sharp-libvips-linux-arm64': 1.0.2 optional: true - '@img/sharp-linux-arm@0.33.3': + '@img/sharp-linux-arm@0.33.4': optionalDependencies: '@img/sharp-libvips-linux-arm': 1.0.2 optional: true - '@img/sharp-linux-s390x@0.33.3': + '@img/sharp-linux-s390x@0.33.4': optionalDependencies: '@img/sharp-libvips-linux-s390x': 1.0.2 optional: true - '@img/sharp-linux-x64@0.33.3': + '@img/sharp-linux-x64@0.33.4': optionalDependencies: '@img/sharp-libvips-linux-x64': 1.0.2 optional: true - '@img/sharp-linuxmusl-arm64@0.33.3': + '@img/sharp-linuxmusl-arm64@0.33.4': optionalDependencies: '@img/sharp-libvips-linuxmusl-arm64': 1.0.2 optional: true - '@img/sharp-linuxmusl-x64@0.33.3': + '@img/sharp-linuxmusl-x64@0.33.4': optionalDependencies: '@img/sharp-libvips-linuxmusl-x64': 1.0.2 optional: true - '@img/sharp-wasm32@0.33.3': + '@img/sharp-wasm32@0.33.4': dependencies: '@emnapi/runtime': 1.1.1 optional: true - '@img/sharp-win32-ia32@0.33.3': + '@img/sharp-win32-ia32@0.33.4': optional: true - '@img/sharp-win32-x64@0.33.3': + '@img/sharp-win32-x64@0.33.4': optional: true '@ioredis/commands@1.2.0': {} @@ -9367,7 +9360,7 @@ snapshots: '@sindresorhus/is@4.6.0': {} - '@sindresorhus/is@6.3.0': {} + '@sindresorhus/is@6.3.1': {} '@sindresorhus/merge-streams@4.0.0': {} @@ -9869,15 +9862,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5)': + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 5.62.0(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/parser': 5.62.0(eslint@9.3.0)(typescript@5.4.5) '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@9.2.0)(typescript@5.4.5) - '@typescript-eslint/utils': 5.62.0(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/type-utils': 5.62.0(eslint@9.3.0)(typescript@5.4.5) + '@typescript-eslint/utils': 5.62.0(eslint@9.3.0)(typescript@5.4.5) debug: 4.3.4(supports-color@8.1.1) - eslint: 9.2.0 + eslint: 9.3.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare-lite: 1.4.0 @@ -9888,16 +9881,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5)': + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.21.0(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/parser': 6.21.0(eslint@9.3.0)(typescript@5.4.5) '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@9.2.0)(typescript@5.4.5) - '@typescript-eslint/utils': 6.21.0(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/type-utils': 6.21.0(eslint@9.3.0)(typescript@5.4.5) + '@typescript-eslint/utils': 6.21.0(eslint@9.3.0)(typescript@5.4.5) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.3.4(supports-color@8.1.1) - eslint: 9.2.0 + eslint: 9.3.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 @@ -9920,26 +9913,26 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@5.62.0(eslint@9.2.0)(typescript@5.4.5)': + '@typescript-eslint/parser@5.62.0(eslint@9.3.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) debug: 4.3.4(supports-color@8.1.1) - eslint: 9.2.0 + eslint: 9.3.0 optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@6.21.0(eslint@9.2.0)(typescript@5.4.5)': + '@typescript-eslint/parser@6.21.0(eslint@9.3.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.3.4(supports-color@8.1.1) - eslint: 9.2.0 + eslint: 9.3.0 optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: @@ -9967,24 +9960,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@5.62.0(eslint@9.2.0)(typescript@5.4.5)': + '@typescript-eslint/type-utils@5.62.0(eslint@9.3.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) - '@typescript-eslint/utils': 5.62.0(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/utils': 5.62.0(eslint@9.3.0)(typescript@5.4.5) debug: 4.3.4(supports-color@8.1.1) - eslint: 9.2.0 + eslint: 9.3.0 tsutils: 3.21.0(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@6.21.0(eslint@9.2.0)(typescript@5.4.5)': + '@typescript-eslint/type-utils@6.21.0(eslint@9.3.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) - '@typescript-eslint/utils': 6.21.0(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/utils': 6.21.0(eslint@9.3.0)(typescript@5.4.5) debug: 4.3.4(supports-color@8.1.1) - eslint: 9.2.0 + eslint: 9.3.0 ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 @@ -10055,30 +10048,30 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@5.62.0(eslint@9.2.0)(typescript@5.4.5)': + '@typescript-eslint/utils@5.62.0(eslint@9.3.0)(typescript@5.4.5)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.3.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) - eslint: 9.2.0 + eslint: 9.3.0 eslint-scope: 5.1.1 semver: 7.6.2 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/utils@6.21.0(eslint@9.2.0)(typescript@5.4.5)': + '@typescript-eslint/utils@6.21.0(eslint@9.3.0)(typescript@5.4.5)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.3.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) - eslint: 9.2.0 + eslint: 9.3.0 semver: 7.6.2 transitivePeerDependencies: - supports-color @@ -10096,9 +10089,9 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-vue@5.0.4(vite@5.2.11(@types/node@20.12.12)(sass@1.77.1)(stylus@0.57.0)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))': + '@vitejs/plugin-vue@5.0.4(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(stylus@0.57.0)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))': dependencies: - vite: 5.2.11(@types/node@20.12.12)(sass@1.77.1)(stylus@0.57.0)(terser@5.31.0) + vite: 5.2.11(@types/node@20.12.12)(sass@1.77.2)(stylus@0.57.0)(terser@5.31.0) vue: 3.4.27(typescript@5.4.5) '@volar/language-core@2.2.4': @@ -10152,7 +10145,7 @@ snapshots: '@vue/compiler-dom': 3.4.27 '@vue/shared': 3.4.27 - '@vue/language-core@2.0.18(typescript@5.4.5)': + '@vue/language-core@2.0.19(typescript@5.4.5)': dependencies: '@volar/language-core': 2.2.4 '@vue/compiler-dom': 3.4.27 @@ -10469,7 +10462,7 @@ snapshots: dependencies: possible-typed-array-names: 1.0.0 - aws-sdk@2.1621.0: + aws-sdk@2.1623.0: dependencies: buffer: 4.9.2 events: 1.1.1 @@ -10638,8 +10631,8 @@ snapshots: browserslist@4.23.0: dependencies: - caniuse-lite: 1.0.30001618 - electron-to-chromium: 1.4.772 + caniuse-lite: 1.0.30001620 + electron-to-chromium: 1.4.774 node-releases: 2.0.14 update-browserslist-db: 1.0.16(browserslist@4.23.0) @@ -10692,7 +10685,7 @@ snapshots: dependencies: semver: 7.6.2 - bull@4.12.4: + bull@4.12.6: dependencies: cron-parser: 4.9.0 get-port: 5.1.1 @@ -10721,10 +10714,10 @@ snapshots: cacheable-lookup@https://codeload.github.com/TheEssem/cacheable-lookup/tar.gz/dd2fb616366a3c68dcf321a57a67295967b204bf: {} - cacheable-request@10.2.14: + cacheable-request@12.0.1: dependencies: '@types/http-cache-semantics': 4.0.4 - get-stream: 6.0.1 + get-stream: 9.0.1 http-cache-semantics: 4.1.1 keyv: 4.5.4 mimic-response: 4.0.0 @@ -10761,7 +10754,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001618: {} + caniuse-lite@1.0.30001620: {} canonicalize@1.0.8: {} @@ -10781,10 +10774,6 @@ snapshots: optionalDependencies: cbor-extract: 2.2.0 - chalk-template@1.1.0: - dependencies: - chalk: 5.3.0 - chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -10810,26 +10799,26 @@ snapshots: character-reference-invalid@1.1.4: {} - chart.js@4.4.2: + chart.js@4.4.3: dependencies: '@kurkle/color': 0.3.2 - chartjs-adapter-date-fns@3.0.0(chart.js@4.4.2)(date-fns@3.6.0): + chartjs-adapter-date-fns@3.0.0(chart.js@4.4.3)(date-fns@3.6.0): dependencies: - chart.js: 4.4.2 + chart.js: 4.4.3 date-fns: 3.6.0 - chartjs-chart-matrix@2.0.1(chart.js@4.4.2): + chartjs-chart-matrix@2.0.1(chart.js@4.4.3): dependencies: - chart.js: 4.4.2 + chart.js: 4.4.3 - chartjs-plugin-gradient@0.6.1(chart.js@4.4.2): + chartjs-plugin-gradient@0.6.1(chart.js@4.4.3): dependencies: - chart.js: 4.4.2 + chart.js: 4.4.3 - chartjs-plugin-zoom@2.0.1(chart.js@4.4.2): + chartjs-plugin-zoom@2.0.1(chart.js@4.4.3): dependencies: - chart.js: 4.4.2 + chart.js: 4.4.3 hammerjs: 2.0.8 cheerio@0.22.0: @@ -11422,7 +11411,7 @@ snapshots: dependencies: jake: 10.9.1 - electron-to-chromium@1.4.772: {} + electron-to-chromium@1.4.774: {} emittery@0.13.1: {} @@ -11510,7 +11499,7 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@1.5.2: {} + es-module-lexer@1.5.3: {} es-object-atoms@1.0.0: dependencies: @@ -11590,18 +11579,18 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-compat-utils@0.5.0(eslint@9.2.0): + eslint-compat-utils@0.5.0(eslint@9.3.0): dependencies: - eslint: 9.2.0 + eslint: 9.3.0 semver: 7.6.2 - eslint-config-prettier@8.10.0(eslint@9.2.0): + eslint-config-prettier@8.10.0(eslint@9.3.0): dependencies: - eslint: 9.2.0 + eslint: 9.3.0 - eslint-config-prettier@9.1.0(eslint@9.2.0): + eslint-config-prettier@9.1.0(eslint@9.3.0): dependencies: - eslint: 9.2.0 + eslint: 9.3.0 eslint-config-standard@16.0.3(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@4.9.4))(eslint@8.57.0))(eslint-plugin-node@11.1.0(eslint@8.57.0))(eslint-plugin-promise@6.1.1(eslint@8.57.0))(eslint@8.57.0): dependencies: @@ -11639,32 +11628,32 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@9.2.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@9.2.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@9.3.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@9.3.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 5.62.0(eslint@9.2.0)(typescript@5.4.5) - eslint: 9.2.0 + '@typescript-eslint/parser': 5.62.0(eslint@9.3.0)(typescript@5.4.5) + eslint: 9.3.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@9.2.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@9.2.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@9.3.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@9.3.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@9.2.0)(typescript@5.4.5) - eslint: 9.2.0 + '@typescript-eslint/parser': 6.21.0(eslint@9.3.0)(typescript@5.4.5) + eslint: 9.3.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-es-x@7.6.0(eslint@9.2.0): + eslint-plugin-es-x@7.6.0(eslint@9.3.0): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.3.0) '@eslint-community/regexpp': 4.10.0 - eslint: 9.2.0 - eslint-compat-utils: 0.5.0(eslint@9.2.0) + eslint: 9.3.0 + eslint-compat-utils: 0.5.0(eslint@9.3.0) eslint-plugin-es@3.0.1(eslint@8.57.0): dependencies: @@ -11672,22 +11661,22 @@ snapshots: eslint-utils: 2.1.0 regexpp: 3.2.0 - eslint-plugin-es@4.1.0(eslint@9.2.0): + eslint-plugin-es@4.1.0(eslint@9.3.0): dependencies: - eslint: 9.2.0 + eslint: 9.3.0 eslint-utils: 2.1.0 regexpp: 3.2.0 - eslint-plugin-eslint-comments@3.2.0(eslint@9.2.0): + eslint-plugin-eslint-comments@3.2.0(eslint@9.3.0): dependencies: escape-string-regexp: 1.0.5 - eslint: 9.2.0 + eslint: 9.3.0 ignore: 5.3.1 - eslint-plugin-file-progress@1.4.0(eslint@9.2.0): + eslint-plugin-file-progress@1.4.0(eslint@9.3.0): dependencies: chalk: 4.1.2 - eslint: 9.2.0 + eslint: 9.3.0 ora: 5.4.1 eslint-plugin-html@7.1.0: @@ -11721,7 +11710,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -11729,9 +11718,9 @@ snapshots: array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.2.0 + eslint: 9.3.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@9.2.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@9.2.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@9.3.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@9.3.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -11742,13 +11731,13 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 5.62.0(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/parser': 5.62.0(eslint@9.3.0)(typescript@5.4.5) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -11756,9 +11745,9 @@ snapshots: array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.2.0 + eslint: 9.3.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@9.2.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@9.2.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@9.3.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@9.3.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -11769,20 +11758,20 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@9.2.0)(typescript@5.4.5) + '@typescript-eslint/parser': 6.21.0(eslint@9.3.0)(typescript@5.4.5) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsdoc@48.2.5(eslint@9.2.0): + eslint-plugin-jsdoc@48.2.5(eslint@9.3.0): dependencies: '@es-joy/jsdoccomment': 0.43.0 are-docs-informative: 0.0.2 comment-parser: 1.4.1 debug: 4.3.4(supports-color@8.1.1) escape-string-regexp: 4.0.0 - eslint: 9.2.0 + eslint: 9.3.0 esquery: 1.5.0 is-builtin-module: 3.2.1 semver: 7.6.2 @@ -11790,42 +11779,42 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-jsonc@2.15.1(eslint@9.2.0): + eslint-plugin-jsonc@2.15.1(eslint@9.3.0): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0) - eslint: 9.2.0 - eslint-compat-utils: 0.5.0(eslint@9.2.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.3.0) + eslint: 9.3.0 + eslint-compat-utils: 0.5.0(eslint@9.3.0) espree: 9.6.1 graphemer: 1.4.0 jsonc-eslint-parser: 2.4.0 natural-compare: 1.4.0 synckit: 0.6.2 - eslint-plugin-markdown@3.0.1(eslint@9.2.0): + eslint-plugin-markdown@3.0.1(eslint@9.3.0): dependencies: - eslint: 9.2.0 + eslint: 9.3.0 mdast-util-from-markdown: 0.8.5 transitivePeerDependencies: - supports-color - eslint-plugin-n@15.7.0(eslint@9.2.0): + eslint-plugin-n@15.7.0(eslint@9.3.0): dependencies: builtins: 5.1.0 - eslint: 9.2.0 - eslint-plugin-es: 4.1.0(eslint@9.2.0) - eslint-utils: 3.0.0(eslint@9.2.0) + eslint: 9.3.0 + eslint-plugin-es: 4.1.0(eslint@9.3.0) + eslint-utils: 3.0.0(eslint@9.3.0) ignore: 5.3.1 is-core-module: 2.13.1 minimatch: 3.1.2 resolve: 1.22.8 semver: 7.6.2 - eslint-plugin-n@16.6.2(eslint@9.2.0): + eslint-plugin-n@16.6.2(eslint@9.3.0): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.3.0) builtins: 5.1.0 - eslint: 9.2.0 - eslint-plugin-es-x: 7.6.0(eslint@9.2.0) + eslint: 9.3.0 + eslint-plugin-es-x: 7.6.0(eslint@9.3.0) get-tsconfig: 4.7.5 globals: 13.24.0 ignore: 5.3.1 @@ -11845,35 +11834,35 @@ snapshots: resolve: 1.22.8 semver: 6.3.1 - eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@9.2.0))(eslint@9.2.0)(prettier@2.8.8): + eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@9.3.0))(eslint@9.3.0)(prettier@2.8.8): dependencies: - eslint: 9.2.0 + eslint: 9.3.0 prettier: 2.8.8 prettier-linter-helpers: 1.0.0 optionalDependencies: - eslint-config-prettier: 8.10.0(eslint@9.2.0) + eslint-config-prettier: 8.10.0(eslint@9.3.0) - eslint-plugin-prettier@5.1.3(@types/eslint@8.56.10)(eslint-config-prettier@9.1.0(eslint@9.2.0))(eslint@9.2.0)(prettier@2.8.8): + eslint-plugin-prettier@5.1.3(@types/eslint@8.56.10)(eslint-config-prettier@9.1.0(eslint@9.3.0))(eslint@9.3.0)(prettier@2.8.8): dependencies: - eslint: 9.2.0 + eslint: 9.3.0 prettier: 2.8.8 prettier-linter-helpers: 1.0.0 synckit: 0.8.8 optionalDependencies: '@types/eslint': 8.56.10 - eslint-config-prettier: 9.1.0(eslint@9.2.0) + eslint-config-prettier: 9.1.0(eslint@9.3.0) - eslint-plugin-promise@5.2.0(eslint@9.2.0): + eslint-plugin-promise@5.2.0(eslint@9.3.0): dependencies: - eslint: 9.2.0 + eslint: 9.3.0 eslint-plugin-promise@6.1.1(eslint@8.57.0): dependencies: eslint: 8.57.0 - eslint-plugin-promise@6.1.1(eslint@9.2.0): + eslint-plugin-promise@6.1.1(eslint@9.3.0): dependencies: - eslint: 9.2.0 + eslint: 9.3.0 eslint-plugin-standard@5.0.0(eslint@8.57.0): dependencies: @@ -11884,13 +11873,13 @@ snapshots: '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - eslint-plugin-unicorn@40.1.0(eslint@9.2.0): + eslint-plugin-unicorn@40.1.0(eslint@9.3.0): dependencies: '@babel/helper-validator-identifier': 7.24.5 ci-info: 3.9.0 clean-regexp: 1.0.0 - eslint: 9.2.0 - eslint-utils: 3.0.0(eslint@9.2.0) + eslint: 9.3.0 + eslint-utils: 3.0.0(eslint@9.3.0) esquery: 1.5.0 indent-string: 4.0.0 is-builtin-module: 3.2.1 @@ -11902,13 +11891,13 @@ snapshots: semver: 7.6.2 strip-indent: 3.0.0 - eslint-plugin-unicorn@45.0.2(eslint@9.2.0): + eslint-plugin-unicorn@45.0.2(eslint@9.3.0): dependencies: '@babel/helper-validator-identifier': 7.24.5 - '@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.3.0) ci-info: 3.9.0 clean-regexp: 1.0.0 - eslint: 9.2.0 + eslint: 9.3.0 esquery: 1.5.0 indent-string: 4.0.0 is-builtin-module: 3.2.1 @@ -11924,40 +11913,40 @@ snapshots: eslint-plugin-vitest-globals@1.5.0: {} - eslint-plugin-vue-scoped-css@2.8.0(eslint@9.2.0)(vue-eslint-parser@9.4.2(eslint@9.2.0)): + eslint-plugin-vue-scoped-css@2.8.0(eslint@9.3.0)(vue-eslint-parser@9.4.2(eslint@9.3.0)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0) - eslint: 9.2.0 - eslint-compat-utils: 0.5.0(eslint@9.2.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.3.0) + eslint: 9.3.0 + eslint-compat-utils: 0.5.0(eslint@9.3.0) lodash: 4.17.21 postcss: 8.4.38 postcss-safe-parser: 6.0.0(postcss@8.4.38) postcss-scss: 4.0.9(postcss@8.4.38) postcss-selector-parser: 6.0.16 postcss-styl: 0.12.3 - vue-eslint-parser: 9.4.2(eslint@9.2.0) + vue-eslint-parser: 9.4.2(eslint@9.3.0) transitivePeerDependencies: - supports-color - eslint-plugin-vue@9.26.0(eslint@9.2.0): + eslint-plugin-vue@9.26.0(eslint@9.3.0): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0) - eslint: 9.2.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.3.0) + eslint: 9.3.0 globals: 13.24.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.0.16 semver: 7.6.2 - vue-eslint-parser: 9.4.2(eslint@9.2.0) + vue-eslint-parser: 9.4.2(eslint@9.3.0) xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color - eslint-plugin-yml@1.14.0(eslint@9.2.0): + eslint-plugin-yml@1.14.0(eslint@9.3.0): dependencies: debug: 4.3.4(supports-color@8.1.1) - eslint: 9.2.0 - eslint-compat-utils: 0.5.0(eslint@9.2.0) + eslint: 9.3.0 + eslint-compat-utils: 0.5.0(eslint@9.3.0) lodash: 4.17.21 natural-compare: 1.4.0 yaml-eslint-parser: 1.2.2 @@ -11985,9 +11974,9 @@ snapshots: dependencies: eslint-visitor-keys: 1.3.0 - eslint-utils@3.0.0(eslint@9.2.0): + eslint-utils@3.0.0(eslint@9.3.0): dependencies: - eslint: 9.2.0 + eslint: 9.3.0 eslint-visitor-keys: 2.1.0 eslint-visitor-keys@1.3.0: {} @@ -12041,15 +12030,15 @@ snapshots: transitivePeerDependencies: - supports-color - eslint@9.2.0: + eslint@9.3.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.3.0) '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 3.0.2 - '@eslint/js': 9.2.0 + '@eslint/eslintrc': 3.1.0 + '@eslint/js': 9.3.0 '@humanwhocodes/config-array': 0.13.0 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.2.4 + '@humanwhocodes/retry': 0.3.0 '@nodelib/fs.walk': 1.2.8 ajv: 6.12.6 chalk: 4.1.2 @@ -12498,7 +12487,7 @@ snapshots: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 5.0.1 + minimatch: 5.1.6 once: 1.4.0 globals@11.12.0: {} @@ -12555,12 +12544,12 @@ snapshots: p-cancelable: 2.1.1 responselike: 2.0.1 - got@14.2.1: + got@14.3.0: dependencies: - '@sindresorhus/is': 6.3.0 + '@sindresorhus/is': 6.3.1 '@szmarczak/http-timer': 5.0.1 cacheable-lookup: 7.0.0 - cacheable-request: 10.2.14 + cacheable-request: 12.0.1 decompress-response: 6.0.0 form-data-encoder: 4.0.2 get-stream: 8.0.1 @@ -13752,7 +13741,7 @@ snapshots: content-disposition: 0.5.4 content-type: 1.0.5 cookies: 0.8.0 - debug: 4.3.3 + debug: 4.3.4(supports-color@8.1.1) delegates: 1.0.0 depd: 2.0.0 destroy: 1.2.0 @@ -15045,7 +15034,7 @@ snapshots: parse-srcset: 1.0.2 postcss: 8.4.38 - sass@1.77.1: + sass@1.77.2: dependencies: chokidar: 3.6.0 immutable: 4.3.6 @@ -15124,14 +15113,14 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 - sharp@0.33.3: + sharp@0.33.4: dependencies: color: 4.2.3 detect-libc: 2.0.3 semver: 7.6.2 optionalDependencies: - '@img/sharp-darwin-arm64': 0.33.3 - '@img/sharp-darwin-x64': 0.33.3 + '@img/sharp-darwin-arm64': 0.33.4 + '@img/sharp-darwin-x64': 0.33.4 '@img/sharp-libvips-darwin-arm64': 1.0.2 '@img/sharp-libvips-darwin-x64': 1.0.2 '@img/sharp-libvips-linux-arm': 1.0.2 @@ -15140,15 +15129,15 @@ snapshots: '@img/sharp-libvips-linux-x64': 1.0.2 '@img/sharp-libvips-linuxmusl-arm64': 1.0.2 '@img/sharp-libvips-linuxmusl-x64': 1.0.2 - '@img/sharp-linux-arm': 0.33.3 - '@img/sharp-linux-arm64': 0.33.3 - '@img/sharp-linux-s390x': 0.33.3 - '@img/sharp-linux-x64': 0.33.3 - '@img/sharp-linuxmusl-arm64': 0.33.3 - '@img/sharp-linuxmusl-x64': 0.33.3 - '@img/sharp-wasm32': 0.33.3 - '@img/sharp-win32-ia32': 0.33.3 - '@img/sharp-win32-x64': 0.33.3 + '@img/sharp-linux-arm': 0.33.4 + '@img/sharp-linux-arm64': 0.33.4 + '@img/sharp-linux-s390x': 0.33.4 + '@img/sharp-linux-x64': 0.33.4 + '@img/sharp-linuxmusl-arm64': 0.33.4 + '@img/sharp-linuxmusl-x64': 0.33.4 + '@img/sharp-wasm32': 0.33.4 + '@img/sharp-win32-ia32': 0.33.4 + '@img/sharp-win32-x64': 0.33.4 shebang-command@1.2.0: dependencies: @@ -15913,16 +15902,16 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 - vite-plugin-compression@0.5.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.1)(stylus@0.57.0)(terser@5.31.0)): + vite-plugin-compression@0.5.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(stylus@0.57.0)(terser@5.31.0)): dependencies: chalk: 4.1.2 debug: 4.3.4(supports-color@8.1.1) fs-extra: 10.1.0 - vite: 5.2.11(@types/node@20.12.12)(sass@1.77.1)(stylus@0.57.0)(terser@5.31.0) + vite: 5.2.11(@types/node@20.12.12)(sass@1.77.2)(stylus@0.57.0)(terser@5.31.0) transitivePeerDependencies: - supports-color - vite@5.2.11(@types/node@20.12.12)(sass@1.77.1)(stylus@0.57.0)(terser@5.31.0): + vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(stylus@0.57.0)(terser@5.31.0): dependencies: esbuild: 0.20.2 postcss: 8.4.38 @@ -15930,7 +15919,7 @@ snapshots: optionalDependencies: '@types/node': 20.12.12 fsevents: 2.3.3 - sass: 1.77.1 + sass: 1.77.2 stylus: 0.57.0 terser: 5.31.0 @@ -15944,10 +15933,10 @@ snapshots: dependencies: '@types/sortablejs': 1.15.8 - vue-eslint-parser@9.4.2(eslint@9.2.0): + vue-eslint-parser@9.4.2(eslint@9.3.0): dependencies: debug: 4.3.4(supports-color@8.1.1) - eslint: 9.2.0 + eslint: 9.3.0 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 @@ -15971,10 +15960,10 @@ snapshots: de-indent: 1.0.2 he: 1.2.0 - vue-tsc@2.0.18(typescript@5.4.5): + vue-tsc@2.0.19(typescript@5.4.5): dependencies: '@volar/typescript': 2.2.4 - '@vue/language-core': 2.0.18(typescript@5.4.5) + '@vue/language-core': 2.0.19(typescript@5.4.5) semver: 7.6.2 typescript: 5.4.5 @@ -16032,7 +16021,7 @@ snapshots: browserslist: 4.23.0 chrome-trace-event: 1.0.3 enhanced-resolve: 5.16.1 - es-module-lexer: 1.5.2 + es-module-lexer: 1.5.3 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 diff --git a/scripts/copy-assets.mjs b/scripts/copy-assets.mjs index 9f667315de..1bfc08d3f2 100644 --- a/scripts/copy-assets.mjs +++ b/scripts/copy-assets.mjs @@ -14,12 +14,12 @@ await (async () => { ]); const locales = (await import("../locales/index.mjs")).default; - const meta = JSON.parse(await fs.readFile(file("built/meta.json"))); + const { version } = JSON.parse(await fs.readFile(file("package.json"))); for await (const [lang, locale] of Object.entries(locales)) { await fs.writeFile( - file(`built/_client_dist_/locales/${lang}.${meta.version}.json`), - JSON.stringify({ ...locale, _version_: meta.version }), + file(`built/_client_dist_/locales/${lang}.${version}.json`), + JSON.stringify({ ...locale, _version_: version }), "utf-8", ); }