diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d8275a7a26..3821fc0178 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: docker.io/rust:slim-bookworm +image: docker.io/rust:bookworm services: - name: docker.io/groonga/pgroonga:latest-alpine-12-slim @@ -47,8 +47,7 @@ variables: default: before_script: - - apt-get update && apt-get -y upgrade - - apt-get -y --no-install-recommends install curl + - apt-get update && apt-get -y --no-install-recommends install curl - curl -fsSL 'https://deb.nodesource.com/setup_18.x' | bash - - apt-get install -y --no-install-recommends build-essential clang mold python3 perl nodejs postgresql-client - corepack enable @@ -111,8 +110,7 @@ test:build:backend_ts: - packages/firefish-js/**/* when: always before_script: - - apt-get update && apt-get -y upgrade - - apt-get -y --no-install-recommends install curl + - apt-get update && apt-get -y --no-install-recommends install curl - curl -fsSL 'https://deb.nodesource.com/setup_18.x' | bash - - apt-get install -y --no-install-recommends build-essential clang mold python3 perl nodejs postgresql-client - corepack enable @@ -159,8 +157,7 @@ test:build:client: when: always services: [] before_script: - - apt-get update && apt-get -y upgrade - - apt-get -y --no-install-recommends install curl + - apt-get update && apt-get -y --no-install-recommends install curl - curl -fsSL 'https://deb.nodesource.com/setup_18.x' | bash - - apt-get install -y --no-install-recommends build-essential python3 perl nodejs - corepack enable @@ -179,10 +176,9 @@ build:container: variables: STORAGE_DRIVER: overlay before_script: - - apt-get update && apt-get -y upgrade + - apt-get update && apt-get install -y --no-install-recommends ca-certificates fuse-overlayfs buildah - |- sed -i -r 's/"version": "([-0-9]+)",/"version": "\1-dev",/' package.json - - apt-get install -y --no-install-recommends ca-certificates fuse-overlayfs buildah - echo "${CI_REGISTRY_PASSWORD}" | buildah login --username "${CI_REGISTRY_USER}" --password-stdin "${CI_REGISTRY}" - export IMAGE_TAG_1="${CI_REGISTRY}/${CI_PROJECT_PATH}/develop:not-for-production" - export IMAGE_TAG_2="${CI_REGISTRY}/${CI_PROJECT_PATH}/develop:not-for-production-$(date +%Y%m%d)" @@ -205,7 +201,7 @@ build:container: cargo:check:msrv: stage: test - image: docker.io/rust:1.74-slim-bookworm + image: docker.io/rust:1.74-bookworm rules: - if: $TEST == 'true' when: always @@ -221,8 +217,7 @@ cargo:check:msrv: when: always services: [] before_script: - - apt-get update && apt-get -y upgrade - - apt-get install -y --no-install-recommends build-essential clang mold python3 perl nodejs postgresql-client + - apt-get update && apt-get install -y --no-install-recommends build-essential clang mold python3 perl nodejs postgresql-client - cp ci/cargo/config.toml /usr/local/cargo/config.toml - export CARGO_TARGET_DIR='ci/target-msrv' script: @@ -264,8 +259,7 @@ cargo:clippy: when: always services: [] before_script: - - apt-get update && apt-get -y upgrade - - apt-get install -y --no-install-recommends build-essential clang mold perl + - apt-get update && apt-get install -y --no-install-recommends build-essential clang mold perl - cp ci/cargo/config.toml /usr/local/cargo/config.toml - rustup component add clippy script: @@ -289,8 +283,7 @@ cargo:doc: when: always services: [] before_script: - - apt-get update - - apt-get install -y --no-install-recommends build-essential clang mold nodejs npm + - apt-get update && apt-get install -y --no-install-recommends build-essential clang mold nodejs npm - cp ci/cargo/config.toml /usr/local/cargo/config.toml script: - cargo doc --document-private-items @@ -331,8 +324,7 @@ clean: - if: $CLEAN && $CI_PIPELINE_SOURCE == 'schedule' services: [] before_script: - - apt-get update && apt-get -y upgrade - - apt-get -y --no-install-recommends install curl + - apt-get update && apt-get -y --no-install-recommends install curl - curl -fsSL 'https://deb.nodesource.com/setup_18.x' | bash - - apt-get install -y --no-install-recommends nodejs - corepack enable diff --git a/Cargo.lock b/Cargo.lock index 974715ab54..887b0b2737 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,6 +214,7 @@ dependencies = [ "cuid2", "emojis", "futures-util", + "identicon-rs", "idna 1.0.2", "image", "isahc", @@ -241,6 +242,7 @@ dependencies = [ "tracing-subscriber", "url", "urlencoding", + "uuid", "web-push", "zhconv", ] @@ -325,6 +327,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e0d60973d9320722cb1206f412740e162a33b8547ea8d6be75d7cff237c7a85" +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -388,9 +396,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.16.1" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" +checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" [[package]] name = "byteorder" @@ -406,9 +414,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "castaway" @@ -418,9 +426,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" dependencies = [ "jobserver", "libc", @@ -611,6 +619,12 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -684,9 +698,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.73+curl-8.8.0" +version = "0.4.74+curl-8.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "450ab250ecf17227c39afb9a2dd9261dc0035cb80f2612472fc0c4aac2dcb84d" +checksum = "8af10b986114528fcdc4b63b6f5f021b7057618411046a4de2ba0f0149a097bf" dependencies = [ "cc", "libc", @@ -758,17 +772,6 @@ dependencies = [ "serde", ] -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "diff" version = "0.1.13" @@ -846,6 +849,18 @@ dependencies = [ "getrandom", ] +[[package]] +name = "educe" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bd92664bf78c4d3dba9b7cdafce6fa15b13ed3ed16175218196942e99168a8" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "either" version = "1.13.0" @@ -894,6 +909,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -927,6 +962,22 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -963,9 +1014,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" dependencies = [ "crc32fast", "miniz_oxide", @@ -1165,6 +1216,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1290,7 +1351,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -1420,6 +1481,17 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "identicon-rs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c4fdcf9f82fef33187cc52ad515de24786ec837837e69f641d1cb8157f4f02" +dependencies = [ + "image", + "sha3", + "thiserror", +] + [[package]] name = "idna" version = "0.5.0" @@ -1451,11 +1523,14 @@ dependencies = [ "bytemuck", "byteorder-lite", "color_quant", + "exr", "gif", "image-webp", "num-traits", "png", + "qoi", "ravif", + "rayon", "rgb", "tiff", "zune-core", @@ -1480,9 +1555,9 @@ checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown", @@ -1663,6 +1738,12 @@ dependencies = [ "spin", ] +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.155" @@ -2439,9 +2520,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "pretty_assertions" @@ -2514,6 +2598,15 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quick-error" version = "2.0.1" @@ -2631,9 +2724,9 @@ dependencies = [ [[package]] name = "redis" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cc5b667390cb038bc65fc4b18c06e2550469f7e06a02d886f1a018a11f63563" +checksum = "e902a69d09078829137b4a5d9d082e0490393537badd7c91a3d69d14639e115f" dependencies = [ "arc-swap", "async-trait", @@ -2670,9 +2763,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -2709,9 +2802,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.45" +version = "0.8.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade4539f42266ded9e755c605bdddf546242b2c961b03b06a7375260788a0523" +checksum = "e12bc8d2f72df26a5d3178022df33720fbede0d31d82c7291662eff89836994d" dependencies = [ "bytemuck", ] @@ -2906,9 +2999,9 @@ dependencies = [ [[package]] name = "sea-orm" -version = "0.12.15" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8814e37dc25de54398ee62228323657520b7f29713b8e238649385dbe473ee0" +checksum = "f9d4ec1cdd8bdd3553d3c946079f58efa33fedc477f32603652652abcef96fe6" dependencies = [ "async-stream", "async-trait", @@ -2922,7 +3015,7 @@ dependencies = [ "serde", "serde_json", "sqlx", - "strum 0.25.0", + "strum 0.26.3", "thiserror", "time", "tracing", @@ -2932,9 +3025,9 @@ dependencies = [ [[package]] name = "sea-orm-macros" -version = "0.12.15" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e115c6b078e013aa963cc2d38c196c2c40b05f03d0ac872fe06b6e0d5265603" +checksum = "f363ead48b625a6f8f905322a820464f728fa4fe4f1c222bed5234ccf8fb8555" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -2946,12 +3039,12 @@ dependencies = [ [[package]] name = "sea-query" -version = "0.30.7" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4166a1e072292d46dc91f31617c2a1cdaf55a8be4b5c9f4bf2ba248e3ac4999b" +checksum = "7e5073b2cfed767511a57d18115f3b3d8bcb5690bf8c89518caec6cb22c0cd74" dependencies = [ "chrono", - "derivative", + "educe", "inherent", "ordered-float", "serde_json", @@ -2959,9 +3052,9 @@ dependencies = [ [[package]] name = "sea-query-binder" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36bbb68df92e820e4d5aeb17b4acd5cc8b5d18b2c36a4dd6f4626aabfa7ab1b9" +checksum = "754965d4aee6145bec25d0898e5c931e6c22859789ce62fd85a42a15ed5a8ce3" dependencies = [ "chrono", "sea-query", @@ -3002,18 +3095,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" dependencies = [ "proc-macro2", "quote", @@ -3022,11 +3115,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -3454,9 +3548,9 @@ dependencies = [ [[package]] name = "strum" -version = "0.25.0" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" [[package]] name = "strum_macros" @@ -3524,15 +3618,14 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.30.13" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" +checksum = "d4115055da5f572fff541dd0c4e61b0262977f453cc9fe04be83aba25a89bdab" dependencies = [ - "cfg-if", "core-foundation-sys", "libc", + "memchr", "ntapi", - "once_cell", "windows", ] @@ -3551,18 +3644,19 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.15" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53" dependencies = [ "cfg-if", "fastrand 2.1.0", + "once_cell", "rustix", "windows-sys 0.52.0", ] @@ -3753,9 +3847,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", @@ -3765,18 +3859,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.17" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap", "serde", @@ -3947,6 +4041,8 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ + "getrandom", + "rand", "serde", ] @@ -4074,8 +4170,9 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-push" -version = "0.10.1" -source = "git+https://github.com/pimeys/rust-web-push.git?rev=40febe4085e3cef9cdfd539c315e3e945aba0656#40febe4085e3cef9cdfd539c315e3e945aba0656" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49214685777c429ac44a92780983f4fadeecddd8c08c463d8196521e17e974bd" dependencies = [ "async-trait", "base64 0.13.1", @@ -4139,11 +4236,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.52.0" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" dependencies = [ - "windows-core", + "windows-core 0.57.0", "windows-targets 0.52.6", ] @@ -4156,6 +4253,49 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -4297,9 +4437,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.16" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -4352,6 +4492,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] @@ -4457,9 +4598,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", @@ -4471,6 +4612,15 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + [[package]] name = "zune-jpeg" version = "0.4.13" diff --git a/Cargo.toml b/Cargo.toml index 421ae8b674..d942cc45c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ convert_case = { version = "0.6.0", default-features = false } cuid2 = { version = "0.1.2", default-features = false } emojis = { version = "0.6.3", default-features = false } futures-util = { version = "0.3.30", default-features = false } +identicon-rs = "5.0.1" idna = { version = "1.0.2", default-features = false } image = { version = "0.25.2", default-features = false } isahc = { version = "1.7.2", default-features = false } @@ -33,15 +34,15 @@ pretty_assertions = { version = "1.4.0", default-features = false } proc-macro2 = { version = "1.0.86", default-features = false } quote = { version = "1.0.36", default-features = false } rand = { version = "0.8.5", default-features = false } -redis = { version = "0.26.0", default-features = false } -regex = { version = "1.10.5", default-features = false } +redis = { version = "0.26.1", default-features = false } +regex = { version = "1.10.6", default-features = false } rmp-serde = { version = "1.3.0", default-features = false } -sea-orm = { version = "0.12.15", default-features = false } -serde = { version = "1.0.204", default-features = false } -serde_json = { version = "1.0.120", default-features = false } +sea-orm = { version = "1.0.0", default-features = false } +serde = { version = "1.0.205", default-features = false } +serde_json = { version = "1.0.122", default-features = false } serde_yaml = { version = "0.9.34", default-features = false } syn = { version = "2.0.72", default-features = false } -sysinfo = { version = "0.30.13", default-features = false } +sysinfo = { version = "0.31.2", default-features = false } thiserror = { version = "1.0.63", default-features = false } tokio = { version = "1.39.2", default-features = false } tokio-test = { version = "0.4.4", default-features = false } @@ -49,7 +50,8 @@ tracing = { version = "0.1.40", default-features = false } tracing-subscriber = { version = "0.3.18", default-features = false } url = { version = "2.5.2", default-features = false } urlencoding = { version = "2.1.3", default-features = false } -web-push = { git = "https://github.com/pimeys/rust-web-push.git", rev = "40febe4085e3cef9cdfd539c315e3e945aba0656", default-features = false } +uuid = "1.10.0" +web-push = { version = "0.10.2", default-features = false } zhconv = "0.3.1" # subdependencies diff --git a/Dockerfile b/Dockerfile index f9087b93b3..9ef65e8fa7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,6 @@ RUN corepack enable && corepack prepare pnpm@latest --activate # Build COPY . ./ RUN pnpm install --frozen-lockfile -RUN cargo fetch --locked --manifest-path Cargo.toml RUN NODE_ENV='production' NODE_OPTIONS='--max_old_space_size=3072' pnpm run build # Trim down the dependencies to only those for production diff --git a/dev/container/docker-compose.yml b/dev/container/docker-compose.yml index 8768a095f3..56f0ff30a9 100644 --- a/dev/container/docker-compose.yml +++ b/dev/container/docker-compose.yml @@ -2,7 +2,7 @@ version: "3" services: web: - image: docker.io/node:18.19.0-bookworm + image: docker.io/node:18.20.0-bookworm container_name: firefish_web restart: unless-stopped depends_on: diff --git a/dev/docs/local-installation.md b/dev/docs/local-installation.md index 70f1d4c489..63197f092e 100644 --- a/dev/docs/local-installation.md +++ b/dev/docs/local-installation.md @@ -15,7 +15,7 @@ sudo apt install build-essential python3 curl wget git lsb-release ### Node.js -Firefish requires Node.js v18.19.0 or later. While you can choose any versions between v18.19.0 and the latest version (v22.2.0 as of writing), we recommend that you install v18.x so as not to use new features inadvertently and introduce incompatibility issues. +Firefish requires Node.js v18.20.0 or later. While you can choose any versions between v18.20.0 and the latest version (v22.2.0 as of writing), we recommend that you install v18.x so as not to use new features inadvertently and introduce incompatibility issues. Instructions can be found at [this repository](https://github.com/nodesource/distributions). diff --git a/docs/changelog.md b/docs/changelog.md index 1f4729fc7f..15610d84ca 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -7,6 +7,15 @@ This changelog is not an exhaustive list. Code refactorings, minor bug fixes, do - Server administrators must check [notice-for-admins.md](https://firefish.dev/firefish/firefish/-/blob/main/docs/notice-for-admins.md) as well. - Third-party client/bot developers may want to check [api-change.md](https://firefish.dev/firefish/firefish/-/blob/main/docs/api-change.md) as well. +## [v20240809](https://firefish.dev/firefish/firefish/-/merge_requests/11262/commits) + +- Add writing mode (right-to-left, vertical) support (!11222) +- Fix bugs + +### Breaking change + +The random icon generator has been changed, so your icon will be changed if you haven't set your icon image and random icon generation is enabled on your server. + ## [v20240729](https://firefish.dev/firefish/firefish/-/merge_requests/11214/commits) - Fix bugs (including a medium severity security issue) diff --git a/docs/downgrade.sql b/docs/downgrade.sql index 24f0f4baa8..d52227acc8 100644 --- a/docs/downgrade.sql +++ b/docs/downgrade.sql @@ -1,6 +1,7 @@ BEGIN; DELETE FROM "migrations" WHERE name IN ( + 'SetEmojiPublicUrl1722346019160', 'SetAccessTokenName1722134626110', 'CreateSystemActors1720618854585', 'AddMastodonSubscriptionType1715181461692', @@ -41,6 +42,9 @@ DELETE FROM "migrations" WHERE name IN ( 'RemoveNativeUtilsMigration1705877093218' ); +-- set-emoji-public-url +ALTER TABLE "emoji" ALTER COLUMN "publicUrl" SET DEFAULT ''; + -- addMastodonSubscriptionType ALTER TABLE "sw_subscription" DROP COLUMN "subscriptionTypes"; DROP TYPE "push_subscription_type"; diff --git a/docs/install.md b/docs/install.md index 4ed2268e6f..96197ee83b 100644 --- a/docs/install.md +++ b/docs/install.md @@ -8,7 +8,7 @@ Firefish depends on the following software. ### Runtime dependencies -- At least [NodeJS](https://nodejs.org/en/) v18.19.0 (v20/v22 recommended) +- At least [NodeJS](https://nodejs.org/en/) v18.20.0 (v20/v22 recommended) - At least [PostgreSQL](https://www.postgresql.org/) v12 (v16 recommended) with [PGroonga](https://pgroonga.github.io/) extension - At least [Redis](https://redis.io/) v7 or [Valkey](https://valkey.io/) v7 - Web Proxy (one of the following) diff --git a/docs/notice-for-admins.md b/docs/notice-for-admins.md index 366b0263e0..09a02c4972 100644 --- a/docs/notice-for-admins.md +++ b/docs/notice-for-admins.md @@ -6,6 +6,14 @@ You can skip intermediate versions when upgrading from an old version, but pleas Please take a look at #10947. +## v20240809 + +### For systemd/pm2 users + +Required Node.js version has been bumped from v18.19.0 to v18.20.0. + +As written in the [v20240710 note](https://firefish.dev/firefish/firefish/-/blob/7660050d9938a5a92293bb8acc361a0ef0715912/docs/notice-for-admins.md#v20240710), it is highly recommended that you use an even newer version since v18.20.0 has known vulnerabilities. + ## v20240725 ### For LibreTranslate self-hosters diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 89aede8cdd..9edfb89e2f 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -735,7 +735,7 @@ _notification: pollEnded: Es resultat de la enquesta ja està disponible emptyPushNotificationMessage: Les notificacions push s'han actualitzat youWereInvitedToGroup: "{userName} t'ha convidat a un grup" - reacted: Ha reaccionat a la teva publicació + reacted: han reaccionat a la teva publicació renoted: Ha impulsat la teva publicació voted: Ha votat a la teva enquesta andCountUsers: I {count} usuaris més {acted} @@ -978,7 +978,7 @@ recipient: Destinatari(s) annotation: Comentaris blockedInstances: Servidors bloquejats blockedInstancesDescription: Llista les adreces dels servidors que vols bloquejar. - Els servidors de la llista no podrán comunicarse amb aquests servidors. + Els servidors de la llista no podrán comunicarse amb aquest servidor. hiddenTags: Etiquetes amagades hiddenTagsDescription: 'Enumereu les etiquetes (sense el #) que voleu ocultar de tendències i explorar. Les etiquetes ocultes encara es poden descobrir per altres mitjans.' @@ -1934,6 +1934,12 @@ _permissions: "read:reactions": Consulta les teves reaccions "read:pages": Consulta la teva pàgina "read:page-likes": Veure les pàgines que t'agraden + push: Enviar notificacions emergents + follow: Seguir i deixar de seguir comptes + read: Llegir (llegir línies de temps, notificacions, reaccions, silenciats, informació + dels comptes, etc.) + write: Escriure (escriure publicacions, reaccionar a publicacions, silenciar usuaris, + editar informació dels comptes, etc.) _poll: noOnlyOneChoice: Calen almenys dues opcions canMultipleVote: Permet seleccionar diverses opcions @@ -2228,7 +2234,7 @@ useCdnDescription: Carrega alguns dels recursos estàtics com ara Twemoji des de releaseToReload: Deixa anar per actualitzar reloading: Actualitzant enableTimelineStreaming: Actualitza les línies de temps automàticament -enablePullToRefresh: Activa "Baixa per actualitzar" +enablePullToRefresh: Activa "Baixa per recarregar" pullDownToReload: Baixa per actualitzar pullToRefreshThreshold: Distancia de baixada per actualitzar searchWords: Paraules / ID o adreça URL que vols cercar @@ -2327,3 +2333,10 @@ addAlt4MeTag: "Afegeix automàticament l'etiqueta #Alt4Me a les teves publicacio strongPassword: Bona contrasenya turnOffCatLanguage: Desactiva la conversió al llenguatge de gat announcement: Anunci +_writingMode: + horizontalTB: Horitzontal, de d'alt a baix + verticalRL: Vertical, de dreta a esquerra, lateralment + verticalLRUpright: Vertical, esquerra andreta, cap amunt + verticalRLUpright: Vertical, de dreta i esquerra, cap amunt + verticalLR: Vertical, d'esquerra a dreta, lateralment +writingMode: Mode d'escriptura diff --git a/locales/en-US.yml b/locales/en-US.yml index e3cf890e39..20c50faa53 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -243,7 +243,7 @@ clearCachedFiles: "Clear cache" clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote files?" blockedInstances: "Blocked Servers" blockedInstancesDescription: "List the hostnames of the servers that you want to block. - Listed servers will no longer be able to communicate with this servers." + Listed servers will no longer be able to communicate with this server." silencedInstances: "Silenced Servers" silencedInstancesDescription: "List the hostnames of the servers that you want to silence. Accounts in the listed servers are treated as \"Silenced\", can only make @@ -1200,7 +1200,7 @@ privateDescription: "Make visible for you only" makePrivate: "Make private" makePrivateConfirm: "This operation will send a deletion request to remote servers and change the visibility to private. Proceed?" -enablePullToRefresh: "Enable \"Pull down to refresh\"" +enablePullToRefresh: "Enable “Pull down to reload”" pullToRefreshThreshold: "Pull distance for reloading" pullDownToReload: "Pull down to reload" releaseToReload: "Release to reload" @@ -1211,13 +1211,13 @@ searchWordsDescription: "Enter the search term here to search for posts. Separat words with a space for an AND search, or 'OR' (without quotes) between words for an OR search.\nFor example, 'morning night' will find posts that contain both 'morning' and 'night', and 'morning OR night' will find posts that contain either 'morning' - or 'night' (or both).\nYou can also filter out certain word(s) from the search results, like - 'sleepy -morning -breakfast'. Moreover, you can combine these AND/OR/exclude conditions like - '(morning OR night) sleepy -breakfast'.\nIf you want to search for a sequence of words (e.g., a sentence), - you must put it in double quotes, not to make it an AND search: \"Today I learned\"\ - \n\nIf you want to go to a specific user page or post page, enter the ID or URL - in this field and click the 'Lookup' button. Clicking 'Search' will search for posts - that literally contain the ID/URL." + or 'night' (or both).\nYou can also filter out certain word(s) from the search results, + like 'sleepy -morning -breakfast'. Moreover, you can combine these AND/OR/exclude + conditions like '(morning OR night) sleepy -breakfast'.\nIf you want to search for + a sequence of words (e.g., a sentence), you must put it in double quotes, not to + make it an AND search: \"Today I learned\"\n\nIf you want to go to a specific user + page or post page, enter the ID or URL in this field and click the 'Lookup' button. + Clicking 'Search' will search for posts that literally contain the ID/URL." searchUsers: "Posted by (optional)" searchUsersDescription: "To search for posts by a specific user/server, enter the ID (@user@example.com, or @user for a local user) or domain name (example.com).\n @@ -1240,7 +1240,8 @@ noAltTextWarning: "Some attached file(s) have no description. Did you forget to showNoAltTextWarning: "Show a warning if you attempt to post files without a description" showAddFileDescriptionAtFirstPost: "Automatically open a form to write a description when you attempt to post files without a description" -addAlt4MeTag: "Automatically append #Alt4Me hashtag to your post if attached file has no description" +addAlt4MeTag: "Automatically append #Alt4Me hashtag to your post if attached file + has no description" turnOffCatLanguage: "Turn off cat language conversion" _emojiModPerm: @@ -1356,6 +1357,13 @@ _nsfw: respect: "Hide NSFW media" ignore: "Don't hide NSFW media" force: "Hide all media" +writingMode: "Writing mode" +_writingMode: + horizontalTB: "Horizontal, top to bottom" + verticalLR: "Vertical, left to right, sideways" + verticalRL: "Vertical, right to left, sideways" + verticalLRUpright: "Vertical, left to right, upright" + verticalRLUpright: "Vertical, right to left, upright" _mfm: play: "Play MFM" stop: "Stop MFM" @@ -1668,8 +1676,10 @@ _2fa: removeKeyConfirm: "Really delete the {name} key?" token: "2FA Token" _permissions: - read: "Read (read timelines, notifications, reactions, mutes, account information, etc.)" - write: "Write (make posts, react to posts, mute users, edit account information, etc.)" + read: "Read (read timelines, notifications, reactions, mutes, account information, + etc.)" + write: "Write (make posts, react to posts, mute users, edit account information, + etc.)" push: "Send push notifications" follow: "Follow and unfollow accounts" "read:account": "View your account information" diff --git a/locales/eo.yml b/locales/eo.yml index 6e9bb18d88..8a9aaee83d 100644 --- a/locales/eo.yml +++ b/locales/eo.yml @@ -9,3 +9,24 @@ notifications: Sciigoj username: Uzantnomo password: Pasvorto forgotPassword: Forgesa pasvorto +fetchingAsApObject: Kaptante de Federujo +ok: O kej +noNotes: Ne afiŝoj +noNotifications: Ne sciigiloj +openInWindow: Malfermu en fenestro +profile: Profilo +gotIt: Jam estas! +cancel: Mezfini +noThankYou: Ne dankon +renotedBy: Potenci de {user} +noAccountDescription: Tiu ĉi uzanto eĉ ne skribis ilia biografio. +enterUsername: Tajpu uzantnomon +instance: Servilo +settings: Agordoj +basicSettings: Bazaj Agordoj +otherSettings: Aliaj Agordoj +login: Ensaluti +loggingIn: Ensalutante +logout: Elsaluti +timeline: Templinio +signup: Registru diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 4e8c4edc57..21dbd1c6c4 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1121,6 +1121,13 @@ _nsfw: respect: "閲覧注意のメディアは隠す" ignore: "閲覧注意のメディアを隠さない" force: "常にメディアを隠す" +writingMode: "組み方向" +_writingMode: + horizontalTB: "横組み、上から下" + verticalLR: "縦組み、左から右、英数字90度回転" + verticalRL: "縦組み、右から左、英数字90度回転" + verticalLRUpright: "縦組み、左から右、英数字1字ずつ配置" + verticalRLUpright: "縦組み、右から左、英数字1字ずつ配置" _mfm: cheatSheet: "MFMチートシート" intro: "MFMは、MisskeyやFirefish、Akkomaなどの様々な場所で使用できるマークアップ言語です。ここでは、MFMで使用可能な構文一覧が確認できます。" @@ -1425,6 +1432,10 @@ _permissions: "write:gallery": "ギャラリーを操作する" "read:gallery-likes": "ギャラリーのいいねを見る" "write:gallery-likes": "ギャラリーのいいねを操作する" + read: 読み取り(タイムライン・通知・ミュート・アカウント情報などの読み込み) + write: 書き込み(投稿・リアクション・ミュート・アカウント情報など) + push: プッシュ通知の送信 + follow: アカウントのフォローとフォロー解除 _auth: shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?" shareAccessAsk: "アカウントへのアクセスを許可しますか?" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 2f2c54c7e6..c4f0ee2656 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -139,9 +139,9 @@ settingGuide: "추천 설정" cacheRemoteFiles: "리모트 파일을 캐시" cacheRemoteFilesDescription: "이 설정을 해지하면 리모트 파일을 캐시하지 않고 해당 파일을 직접 링크하게 됩니다. 그에 따라 서버의 저장 공간을 절약할 수 있지만, 썸네일이 생성되지 않기 때문에 통신량이 증가합니다." -flagAsBot: "나는 봇입니다" -flagAsBotDescription: "이 계정을 자동화된 수단으로 운용할 경우에 활성화해 주세요. 이 플래그를 활성화하면, 다른 봇이 이를 참고하여 - 봇 끼리의 무한 연쇄 반응을 회피하거나, 이 계정의 시스템 상에서의 취급이 Bot 운영에 최적화되는 등의 변화가 생깁니다." +flagAsBot: "이 계정은 자동화된 계정입니다" +flagAsBotDescription: "이 계정을 자동화된 수단으로 운용할 경우에 활성화해 주세요. 이 플래그를 활성화하면, 다른 자동화 계정이 + 이를 참고하여 자동화 계정 간 무한 연쇄 반응을 회피하거나, 이 계정의 시스템 상에서의 취급이 자동화 계정 운영에 최적화되는 등의 변화가 생깁니다." flagAsCat: "나는 고양이다냥" flagAsCatDescription: "고양이귀를 쓰고 냥냥거려요!" flagShowTimelineReplies: "타임라인에 게시물의 답글을 표시하기" @@ -261,7 +261,7 @@ agreeTo: "{0}에 동의" tos: "이용 약관" start: "시작하기" home: "홈" -remoteUserCaution: "리모트 유저이기 때문에, 정보가 정확하지 않을 수 있습니다." +remoteUserCaution: "리모트 유저는 모든 정보가 표시되지 않습니다." activity: "활동" images: "이미지" birthday: "생일" @@ -441,8 +441,8 @@ usernameInvalidFormat: "a~z, A~Z, 0-9, _를 사용할 수 있습니다." tooShort: "너무 짧습니다" tooLong: "너무 깁니다" weakPassword: "약한 비밀번호" -normalPassword: "좋은 비밀번호" -veryStrongPassword: "강한 비밀번호" +normalPassword: "보통 비밀번호" +veryStrongPassword: "매우 강한 비밀번호" passwordMatched: "일치합니다" passwordNotMatched: "일치하지 않습니다" signinWith: "{x}로 로그인" @@ -605,7 +605,7 @@ emptyToDisableSmtpAuth: "SMTP 인증을 사용하지 않으려면 공란으로 smtpSecure: "SMTP 연결에 Implicit SSL/TTS 사용" smtpSecureInfo: "STARTTLS 사용 시에는 해제합니다" testEmail: "이메일 전송 테스트" -wordMute: "단어 뮤트" +wordMute: "단어 및 언어 뮤트" regexpError: "정규 표현식 오류" regexpErrorDescription: "{tab}단어 뮤트 {line}행의 정규 표현식에 오류가 발생했습니다:" instanceMute: "서버 뮤트" @@ -798,7 +798,7 @@ customCss: "CSS 사용자화" customCssWarn: "이 설정은 기능을 알고 있는 경우에만 사용해야 합니다. 잘못된 값을 입력하면 클라이언트가 정상적으로 작동하지 않을 수 있습니다." global: "글로벌" -squareAvatars: "프로필 아이콘을 사각형으로 표시" +squareAvatars: "고양이가 아닌 프로필 아이콘을 사각형으로 표시" sent: "전송" received: "수신" searchResult: "검색 결과" @@ -942,7 +942,8 @@ _accountDelete: inProgress: "삭제 진행 중" _ad: back: "뒤로" - reduceFrequencyOfThisAd: "이 광고의 표시 빈도 낮추기" + reduceFrequencyOfThisAd: "이 배너의 표시 빈도 낮추기" + adsBy: '{by}에 의한 커뮤니티 배너' _forgotPassword: enterEmail: "여기에 계정에 등록한 메일 주소를 입력해 주세요. 입력한 메일 주소로 비밀번호 재설정 링크를 발송합니다." ifNoEmail: "메일 주소를 등록하지 않은 경우, 서버 관리자에게 문의해 주십시오." @@ -1122,6 +1123,12 @@ _wordMute: soft: "보통" hard: "보다 높은 수준" mutedNotes: "뮤트된 게시물" + mutePatterns: 뮤트할 패턴 + langDescription: 설정한 언어와 일치하는 게시물을 타임라인에서 숨깁니다. + lang: 언어 + muteLangs: 뮤트할 언어 + muteLangsDescription: OR의 경우 공백 또는 줄바꿈으로 구분하십시오. + muteLangsDescription2: en, fr, ja, zh 와 같은 언어 코드를 입력하세요. _instanceMute: instanceMuteDescription: "뮤트한 서버에서 오는 답글을 포함한 모든 게시물과 부스트를 뮤트합니다." instanceMuteDescription2: "한 줄에 하나씩 입력해 주세요" @@ -1277,6 +1284,10 @@ _permissions: "write:gallery": "갤러리를 추가하거나 삭제합니다" "read:gallery-likes": "갤러리의 좋아요를 확인합니다" "write:gallery-likes": "갤러리에 좋아요를 추가하거나 취소합니다" + push: 푸시 알림 보내기 + read: 읽기 (타임라인, 알림, 리액션, 뮤트, 계정 정보 등) + write: 쓰기 (게시물 작성, 리액션 보내기, 뮤트하기, 계정 정보 수정 등) + follow: 계정 팔로우 및 팔로우 해제 _auth: shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?" shareAccessAsk: "이 애플리케이션이 계정에 접근하는 것을 허용하시겠습니까?" @@ -1760,6 +1771,7 @@ _notification: reacted: 님의 리액션 renoted: 님이 부스트 voted: 님이 투표함 + andCountUsers: 와(과) {count}명이 {acted} _deck: alwaysShowMainColumn: "메인 칼럼 항상 표시" columnAlign: "칼럼 정렬" @@ -1803,7 +1815,7 @@ privateMode: 비공개 모드 audio: 오디오 customKaTeXMacro: 커스텀 KaTeX 매크로 replayTutorial: 튜토리얼 다시 보기 -renoteMute: 부스트 뮤트 +renoteMute: 타임라인에서 부스트 숨기기 antennaInstancesDescription: 서버 호스트를 한 줄에 하나씩 입력하세요 userSaysSomethingReason: '{name} 님이 {reason}에 대해 말했습니다' userSaysSomethingReasonQuote: '{name} 님이 {reason} 을 포함하는 게시물을 인용했습니다' @@ -1874,11 +1886,11 @@ selectChannel: 채널 선택 allowedInstancesDescription: 연합을 허가하려는 서버를 한 줄에 하나씩 입력합니다. 비공개 모드에서만 유효합니다. splash: 스플래시 화면 preventAiLearningDescription: 업로드한 게시물이나 미디어를 AI 모델이 학습하지 말기를 요구합니다. -isBot: 이 계정은 봇입니다 +isBot: 이 계정은 자동화된 계정입니다 isAdmin: 관리자 newer: 새로운 게시물 older: 이전 게시물 -renoteUnmute: 부스트 뮤트 해제 +renoteUnmute: 타임라인에서 부스트 보이기 accountMoved: '이 유저는 다른 계정으로 이사했습니다:' silencedInstances: 사일런스한 서버 accessibility: 접근성 @@ -1917,8 +1929,7 @@ license: 라이선스 migrationConfirm: "정말로 이 계정을 {account}로 이사하시겠습니까? 한 번 이사하면, 현재 이 계정은 두 번 다시 사용할 수 없게 됩니다.\n또한, 이사 갈 계정에 현재 사용 중인 계정의 별칭을 올바르게 작성하였는지 다시 한 번 확인하십시오." noteId: 게시물 ID -signupsDisabled: 현재 이 서버에서는 신규 등록을 받고 있지 않습니다. 초대 코드를 가지고 계신 경우 아래 칸에 입력해 주십시오. 초대 - 코드를 가지고 있지 않더라도, 신규 등록이 열려 있는 다른 서버에 등록하실 수 있습니다! +signupsDisabled: 현재 이 서버에서는 신규 등록을 받고 있지 않습니다. 초대 코드를 가지고 계신 경우 아래 칸에 입력해 주십시오. apps: 앱 preventAiLearning: AI에 의한 학습을 막기 isLocked: 이 계정은 팔로우를 수동으로 승인합니다 @@ -2002,3 +2013,108 @@ renotes: 부스트 quotes: 인용 sentFollowRequests: 팔로우 요청 보냄 reactions: 리액션 +searchUsers: 작성한 사람 (옵션) +replaceChatButtonWithAccountButton: 채팅 버튼을 계정 전환으로 변경 +searchEngine: 검색 MFM에서 사용할 검색 엔진 +turnOffCatLanguage: 냥체 변환을 끄기 +noSentFollowRequests: 승인 대기중인 팔로우 요청이 없습니다 +squareCatAvatars: 고양이 계정의 아이콘을 사각형으로 표시 +useThisAccountConfirm: 이 계정으로 계속 진행하시겠습니까? +inputAccountId: '당신의 계정을 입력해 주세요 (예시: @firefish@info.firefish.dev)' +pullToRefreshThreshold: 새로고침하기 위해 아래로 당길 길이 +pullDownToReload: 아래로 당겨 새로고침 +searchRange: 게시시각 범위 (옵션) +showAddFileDescriptionAtFirstPost: 설명이 없는 첨부파일을 게시하려고 할 때에 자동으로 설명 작성창을 열기 +searchCwAndAlt: CW 주석과 미디어 설명에서도 검색하기 +publishTimelines: 비로그인 유저에게 타임라인을 공개 +addAlt4MeTag: '설명이 없는 파일을 게시할 때에 자동으로 #Alt4Me 해시태그를 포함하여 게시' +_emojiModPerm: + unauthorized: 없음 + full: 모두 허용 + add: 추가 + mod: 추가 및 편집 +openServerInfo: 게시물의 서버 이름을 클릭하여 서버 정보 열기 +iconSet: 아이콘 스타일 +showBigPostButton: 게시물 작성 폼에서 게시 버튼을 매우 크게 하기 +emojiModPerm: 커스텀 이모지 관리 권한 +releaseToReload: 놓아서 새로고침 +searchWords: 검색할 단어 / 조회할 ID 및 URL +_later: + secondsAgo: '{n}초 후' + minutesAgo: '{n}분 후' + hoursAgo: '{n}시간 후' + daysAgo: '{n}일 후' + weeksAgo: '{n}주 후' + monthsAgo: '{n}개월 후' + future: 미래 + justNow: 잠시 후 + yearsAgo: '{n}년 후' +makePrivateConfirm: 리모트 서버에 삭제 요청을 보내고, 게시물의 공개 범위를 '비공개'로 전환합니다. 계속하시겠습니까? +markLocalFilesNsfwByDefault: 모든 로컬 파일을 기본으로 NSFW로 처리 +markLocalFilesNsfwByDefaultDescription: 이 설정이 켜져 있더라도 유저는 직접 NSFW 설정을 해제할 수 있으며, 이 + 설정은 기존 파일에는 적용되지 않습니다. +showAttachedNotes: 이 파일이 첨부된 게시물 보기 +noLanguage: 언어 없음 +toReply: 답글 +toQuote: 인용 +replyMute: 타임라인에서 답글 숨기기 +searchPostsWithFiles: 첨부 파일이 있는 게시물만 검색 +antennaLimit: 각 유저가 생성 가능한 최대 안테나 수 +forMobile: 모바일 +reloading: 새로고침하는 중 +noAltTextWarning: 미디어 설명이 없는 파일이 있습니다. 깜빡하고 안 쓰셨나요? +publishTimelinesDescription: 활성화하면 로그인하지 않아도 {url} 에서 로컬 타임라인과 글로벌 타임라인을 확인할 수 있게 + 됩니다. +cannotEditVisibility: 공개 범위를 변경할 수 없습니다 +private: 비공개 +preventMisclick: 클릭 실수 방지 +strongPassword: 강한 비밀번호 +_iconSets: + light: 얇음 + regular: 보통 + bold: 굵음 +i18nServerInfo: 새 단말부터 {language}가 기본값으로 적용됩니다. +privateDescription: 나를 제외한 모두에게 비공개 +enableTimelineStreaming: 타임라인을 자동으로 갱신 +toPost: 게시 +replyUnmute: 타임라인에서 답글 보이기 +attachedToNotes: 이 파일이 첨부된 게시물 +ipFirstAcknowledged: IP 주소를 처음 발견한 날짜 +driveCapacityOverride: 드라이브 용량 변경 +remoteFollow: 리모트 팔로우 +emojiModPermDescription: "추가: 커스텀 이모지의 신규 추가와 태그/카테고리/라이선스를 신규 지정할 수 있습니다.\n추가 및 편집: + '추가'에 더해, 기존 이모지의 이름/카테고리/태그/라이선스를 변경할 수 있습니다.\n모두 허용: '추가 및 편집'에 더해, 기존 커스텀 이모지의 + 삭제를 허가합니다.\n관리자 및 모더레이터는 이 설정을 무시하고 '모두 허용' 권한이 주어집니다." +searchWordsDescription: "게시물을 검색하시려면, 여기에 검색어를 입력하여 주십시오. 공백으로 구분하여 AND, 'OR'으로 구분하여 + OR 검색이 가능합니다.\n예를 들어, '아침 저녁'이라고 검색할 경우 '아침'과 '저녁'을 동시에 포함하는 게시물을 검색하며, '아침 OR 저녁'이라고 + 검색할 경우 '아침' 또는 '저녁' 둘 중 하나가 포함된 게시물을 검색합니다.\n-(대시) 부호를 붙여 특정 단어를 제외할 수 있으며, AND/OR/제외는 + 괄호를 사용하여 동시에 사용할 수 있습니다. '(아침 OR 저녁) 졸려 -식곤증' 과 같이 사용합니다.\n띄어쓰기가 포함된 문자열을 검색하려는 + 경우 \"오늘 뭐하지\"와 같이 큰따옴표를 사용하십시오.\n\n특정 유저나 게시물 페이지로 이동하시려면, 이 칸에 ID 또는 URL을 입력하고 + '조회'를 눌러 주십시오. '검색'을 누르면 해당 ID 또는 URL이 포함된 게시물을 검색합니다." +searchUsersDescription: "특정 게시자가 올린 게시물을 검색할 경우, @user@example.com 혹은 (로컬 유저의 경우) + @user와 같이 게시자의 ID를 입력하십시오. 도메인을 입력하여 해당 서버의 모든 유저가 작성한 게시물을 검색할 수 있습니다.\n\nme 를 + 입력하면 자신의 게시물을 검색합니다. 자신의 검색 결과에는 미등재, 팔로워, 다이렉트, 비공개 게시물이 모두 포함됩니다.\n\nlocal 을 입력하면 + 이 서버의 게시물을 검색합니다." +searchRangeDescription: "게시 시각을 지정하실 경우, 20220615-20231031 과 같이 입력하십시오.\n\n올해 안에서 + 검색하려면 연도를 생략할 수 있습니다. (0105-0106 또는 20231105-0110 과 같이)\n\n시작 날짜와 종료 날짜를 생략할 수 있습니다. + 예를 들어 -0102 의 경우 올해 1월 2일까지의 게시물을, 20231026- 의 경우 2023년 10월 26일 이후의 게시물을 검색합니다." +showNoAltTextWarning: 설명이 없는 첨부 파일을 게시할 때에 경고 +vibrate: 진동 활성화 +clickToShowPatterns: 클릭하여 트랙을 표시 +announcement: 공지사항 +toEdit: 편집 +media: 미디어 +i18nServerChange: '{language}를 대신해서 사용합니다.' +i18nServerSet: 새 기기의 표시 언어를 {language} 로 설정합니다. +getQrCode: QR 코드 표시 +copyRemoteFollowUrl: 리모트 팔로우 URL 복사 +useCdn: 일부 구성요소를 CDN에서 가져오기 +useCdnDescription: Twemoji와 같은 일부 정적 구성요소를 JSDelivr CDN에서 가져옵니다. 비활성화할 경우 이 서버에서 가져옵니다. +suggested: 추천 +showPreviewByDefault: 게시물 작성 화면에서 항상 미리보기 켜기 +hideFollowButtons: 잘못 누르기 쉬운 위치에 있는 팔로우 버튼을 숨기기 +replaceWidgetsButtonWithReloadButton: 위젯 버튼을 새로고침으로 변경 +postSearch: 이 서버의 게시물 검색 +makePrivate: 비공개로 전환 +enablePullToRefresh: "'아래로 당겨 새로고침'을 활성화" +moderationNote: 모더레이션 노트 diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 3d80c0fcfd..256aea2151 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -478,7 +478,7 @@ existingAccount: "现有的账号" regenerate: "重新生成" fontSize: "字体大小" noFollowRequests: "没有待批准的关注申请" -noSentFollowRequests: "没有待回应的关注请求" +noSentFollowRequests: "尚未发送任何关注请求" openImageInNewTab: "在新标签页中打开图片" dashboard: "管理面板" local: "本地" @@ -1012,6 +1012,13 @@ _nsfw: respect: "隐藏敏感内容" ignore: "不隐藏敏感内容" force: "总是隐藏内容" +writingMode: "书写方向" +_writingMode: + horizontalTB: "横排,由上向下" + verticalLR: "直排,由左向右,字母旋转90°" + verticalRL: "直排,由右向左,字母旋转90°" + verticalLRUpright: "直排,由左向右,字母逐个排列" + verticalRLUpright: "直排,由右向左,字母逐个排列" _mfm: cheatSheet: "MFM 代码速查表" intro: "MFM 是一种在 Misskey、Firefish、Akkoma 中使用的标记语言,可以在很多地方使用。您可以在此处查看所有可用的 MFM 语法的列表。" @@ -1322,6 +1329,10 @@ _permissions: "write:gallery": "编辑图库" "read:gallery-likes": "读取喜欢的图片" "write:gallery-likes": "编辑喜欢的图片" + read: 读取(时间线、通知、回应、静音、账户信息等) + write: 修改(发表帖子、回应帖子、静音用户、编辑用户信息等) + push: 发送推送通知 + follow: 关注和取消关注账号 _auth: shareAccess: "您要授权允许 \"{name}\" 访问您的账号吗?" shareAccessAsk: "您确定要授权此应用访问您的账号吗?" @@ -1990,7 +2001,7 @@ origin: 起源 confirm: 确认 importZip: 导入 ZIP exportZip: 导出 ZIP -getQrCode: "获取二维码" +getQrCode: "显示二维码" remoteFollow: "远程关注" copyRemoteFollowUrl: "复制远程关注 URL" objectStorageS3ForcePathStyleDesc: 打开此选项可构建格式为 "s3.amazonaws.com//" 而非 ".s3.amazonaws.com" @@ -2027,7 +2038,7 @@ _emojiModPerm: unauthorized: 未授权 full: 全部允许 moreUrls: 置顶页面 -enablePullToRefresh: 启用 “下拉刷新” +enablePullToRefresh: 启用“下拉刷新” pullToRefreshThreshold: 触发下拉刷新所需的距离 pullDownToReload: 下拉刷新 releaseToReload: 释放刷新 @@ -2064,10 +2075,10 @@ searchCwAndAlt: 包括内容警告和文件描述 publishTimelines: 为访客发布时间线 publishTimelinesDescription: 如果启用,在用户登出时本地和全局时间线也会显示在 {url} 上。 searchWordsDescription: "在此处输入搜索词以搜索帖子。交集搜索关键词之间使用空格进行区分,并集搜索关键词之间使用 OR 进行区分。\n例如 - '早上 晚上' 将查找包含 '早上' 和 '晚上' 的帖子,而 '早上 OR 晚上' 将查找包含 '早上' 或 '晚上' (以及同时包含两者)的帖子。\n您还可以组合交集/并集条件,例如 - '(早上 OR 晚上) 困了' 。\n如果您想搜索单词序列(例如一个英语句子),您必须将其放在双引号中,例如 \"Today I learned\" 以区分于交集搜索。\n - \n如果您想转到特定的用户页面或帖子页面,请在此字段中输入用户 ID 或 URL,然后单击 “查询” 按钮。 单击 “搜索” 将搜索字面包含用户 ID/URL - 的帖子。" + '早上 晚上' 将查找包含 '早上' 和 '晚上' 的帖子,而 '早上 OR 晚上' 将查找包含 '早上' 或 '晚上' (以及同时包含两者)的帖子。\n您也可以从搜索结果中过滤部分关键词,例如 + '困了 -早上 -早餐'。此外,您还可以组合交集/并集条件,例如 '(早上 OR 晚上) 困了 -早餐' 。\n如果您想搜索单词序列(例如一个英语句子),您必须将其放在双引号中,例如 + \"Today I learned\" 以区分于交集搜索。\n \n如果您想转到特定的用户页面或帖子页面,请在此字段中输入用户 ID 或 URL,然后单击 “查询” + 按钮。 单击 “搜索” 将搜索字面包含用户 ID/URL 的帖子。" searchRangeDescription: "如果您要过滤时间段,请按以下格式输入:20220615-20231031\n\n如果您省略年份(例如 0105-0106 或 20231105-0110),它将被解释为当前年份。\n\n您还可以省略开始日期或结束日期。 例如 -0102 将过滤搜索结果以仅显示今年 1 月 2 日之前发布的帖子,而 20231026- 将过滤结果以仅显示 2023 年 10 月 26 日之后发布的帖子。" @@ -2076,7 +2087,7 @@ noAltTextWarning: 有些附件没有描述。您是否忘记写描述了? showNoAltTextWarning: 当您尝试发布没有描述的帖子附件时显示警告 showAddFileDescriptionAtFirstPost: 当您首次尝试发布没有描述的帖子附件时自动弹出添加描述页面 autocorrectNoteLanguage: 当帖子语言不符合自动检测的结果的时候显示警告 -incorrectLanguageWarning: "看上去您帖子使用的语言是{detected},但您选择的语言是{current}。\n要改为以{detected}发帖吗?" +incorrectLanguageWarning: "检测到帖子语言可能是{detected}。\n要将发帖语言从{current}更改为{detected}吗?" noteEditHistory: "帖子编辑历史" media: 媒体 slashQuote: "斜杠引用" @@ -2087,3 +2098,6 @@ scheduledPostAt: "帖子将于 {time} 发送" scheduledDate: "发送日期" mergeThreadInTimeline: "将时间线内的连续回复合并成一串" mergeRenotesInTimeline: "合并同一个帖子的转发" +turnOffCatLanguage: 关闭猫语转换 +strongPassword: 密码强度:良好 +addAlt4MeTag: '当附件没有描述时,自动在帖子中添加 #Alt4Me 标签' diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 6c8f1c4727..ba7bedcd7c 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1004,6 +1004,13 @@ _nsfw: respect: "隱藏敏感內容" ignore: "不隱藏敏感內容" force: "隱藏所有內容" +writingMode: "書寫方向" +_writingMode: + horizontalTB: "橫排,由上而下" + verticalLR: "直排,由左而右,字母旋轉90°" + verticalRL: "直排,由右而左,字母旋轉90°" + verticalLRUpright: "直排,由左而右,字母逐個排列" + verticalRLUpright: "直排,由右而左,字母逐個排列" _mfm: cheatSheet: "MFM代碼小抄" intro: "MFM是Misskey、Firefish、Akkoma等專用的標記語言,可以在各個位置使用。 您可以這裏看到MFM可用語法列表。" diff --git a/package.json b/package.json index e6cffd117b..9cefe7c205 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "firefish", - "version": "20240729", + "version": "20240809", "repository": { "type": "git", "url": "https://firefish.dev/firefish/firefish.git" }, - "packageManager": "pnpm@9.6.0", + "packageManager": "pnpm@9.7.0", "private": true, "scripts": { "rebuild": "pnpm run clean && pnpm run build", @@ -47,8 +47,8 @@ "@biomejs/cli-darwin-x64": "1.8.3", "@biomejs/cli-linux-arm64": "1.8.3", "@biomejs/cli-linux-x64": "1.8.3", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "execa": "9.3.0", - "pnpm": "9.6.0" + "pnpm": "9.7.0" } } diff --git a/packages/backend-rs/Cargo.toml b/packages/backend-rs/Cargo.toml index 54efddecbe..9289f3eecb 100644 --- a/packages/backend-rs/Cargo.toml +++ b/packages/backend-rs/Cargo.toml @@ -28,6 +28,7 @@ chrono = { workspace = true } cuid2 = { workspace = true } emojis = { workspace = true } futures-util = { workspace = true, features = ["io"] } +identicon-rs = { workspace = true } idna = { workspace = true, features = ["std", "compiled_data"] } image = { workspace = true, features = ["avif", "bmp", "gif", "ico", "jpeg", "png", "tiff", "webp"] } isahc = { workspace = true, features = ["http2", "text-decoding", "json"] } @@ -42,13 +43,14 @@ sea-orm = { workspace = true, features = ["macros", "runtime-tokio-rustls", "sql serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } serde_yaml = { workspace = true } -sysinfo = { workspace = true } +sysinfo = { workspace = true, features = ["system", "disk"] } thiserror = { workspace = true } tokio = { workspace = true, features = ["fs", "io-std", "io-util", "macros", "process", "rt-multi-thread", "signal", "sync", "time"] } tracing = { workspace = true } tracing-subscriber = { workspace = true, features = ["ansi"] } url = { workspace = true } urlencoding = { workspace = true } +uuid = { workspace = true, features = ["v4", "fast-rng"] } web-push = { workspace = true, features = ["isahc-client"] } zhconv = { workspace = true } diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts index d939266207..f2fdb66bd0 100644 --- a/packages/backend-rs/index.d.ts +++ b/packages/backend-rs/index.d.ts @@ -48,8 +48,6 @@ export interface Acct { export declare function acctToString(acct: Acct): string -export type Activity = 'Follow'; - export interface Ad { id: string createdAt: DateTimeWithTimeZone @@ -106,6 +104,57 @@ export type AntennaSrc = 'all'| 'list'| 'users'; +export interface ApAccept { + id: string + type: ApObject + actor: string + object: ApFollow +} + +export interface ApEmoji { + id: string + type: ApObject + name: string + updated: string + icon: Icon +} + +export interface ApFlag { + type: ApObject + actor: string + content: string + object: string +} + +export interface ApFollow { + id: string + type: ApObject + actor: string + object: string +} + +export interface ApHashtag { + id: string + type: ApObject + name: string +} + +export interface ApMention { + type: ApObject + href: string + name: string +} + +export type ApObject = 'Accept'| +'Emoji'| +'Flag'| +'Follow'| +'Hashtag'| +'Mention'| +'Image'| +'Read'| +'Tombstone'; + export interface App { id: string createdAt: DateTimeWithTimeZone @@ -117,6 +166,17 @@ export interface App { callbackUrl: string | null } +export interface ApRead { + type: ApObject + actor: string + object: string +} + +export interface ApTombstone { + id: string + type: ApObject +} + export interface AttestationChallenge { id: string userId: string @@ -425,13 +485,6 @@ export interface Following { followeeSharedInbox: string | null } -export interface FollowRelay { - id: string - type: Activity - actor: string - object: string -} - export interface FollowRequest { id: string createdAt: DateTimeWithTimeZone @@ -487,6 +540,8 @@ export declare function genId(): string /** Generate an ID using a specific datetime */ export declare function genIdAt(date: Date): string +export declare function genIdenticon(id: string): Promise + export declare function getFullApAccount(username: string, host?: string | undefined | null): string export declare function getImageSizeFromUrl(url: string): Promise @@ -522,6 +577,12 @@ export interface Hashtag { attachedRemoteUsersCount: number } +export interface Icon { + type: ApObject + mediaType: string + url: string +} + export interface IdConfig { length?: number fingerprint?: string @@ -1265,7 +1326,23 @@ export type RelayStatus = 'accepted'| /** Delete all entries in the [attestation_challenge] table created at more than 5 minutes ago */ export declare function removeOldAttestationChallenges(): Promise -export declare function renderFollowRelay(relayId: string): Promise +export declare function renderAccept(userId: string, followObject: ApFollow): ApAccept + +export declare function renderEmoji(emoji: Emoji): ApEmoji + +export declare function renderFlag(targetUserUri: string, comment: string): Promise + +export declare function renderFollow(follower: UserLike, followee: UserLike, requestId?: string | undefined | null): ApFollow + +export declare function renderFollowRelay(relayId: string): Promise + +export declare function renderHashtag(tagName: string): ApHashtag + +export declare function renderMention(user: UserLike): ApMention + +export declare function renderRead(userId: string, messageUri: string): ApRead + +export declare function renderTombstone(noteId: string): ApTombstone export interface RenoteMuting { id: string @@ -1423,8 +1500,6 @@ export declare function updateAntennasOnNewNote(note: Note, noteAuthor: Acct, no export declare function updateMetaCache(): Promise -export declare function updateNodeinfoCache(): Promise - /** Usage statistics for this server. */ export interface Usage { users: Users @@ -1537,6 +1612,13 @@ export interface UserKeypair { privateKey: string } +export interface UserLike { + id: string + username: string + host: string | null + uri: string | null +} + export interface UserList { id: string createdAt: DateTimeWithTimeZone diff --git a/packages/backend-rs/index.js b/packages/backend-rs/index.js index 9ca4963455..0ef803af9e 100644 --- a/packages/backend-rs/index.js +++ b/packages/backend-rs/index.js @@ -362,8 +362,8 @@ if (!nativeBinding) { } module.exports.acctToString = nativeBinding.acctToString -module.exports.Activity = nativeBinding.Activity module.exports.AntennaSrc = nativeBinding.AntennaSrc +module.exports.ApObject = nativeBinding.ApObject module.exports.ChatEvent = nativeBinding.ChatEvent module.exports.ChatIndexEvent = nativeBinding.ChatIndexEvent module.exports.checkWordMute = nativeBinding.checkWordMute @@ -384,6 +384,7 @@ module.exports.generateSecureRandomString = nativeBinding.generateSecureRandomSt module.exports.generateUserToken = nativeBinding.generateUserToken module.exports.genId = nativeBinding.genId module.exports.genIdAt = nativeBinding.genIdAt +module.exports.genIdenticon = nativeBinding.genIdenticon module.exports.getFullApAccount = nativeBinding.getFullApAccount module.exports.getImageSizeFromUrl = nativeBinding.getImageSizeFromUrl module.exports.getInstanceActor = nativeBinding.getInstanceActor @@ -437,7 +438,15 @@ module.exports.PushNotificationKind = nativeBinding.PushNotificationKind module.exports.PushSubscriptionType = nativeBinding.PushSubscriptionType module.exports.RelayStatus = nativeBinding.RelayStatus module.exports.removeOldAttestationChallenges = nativeBinding.removeOldAttestationChallenges +module.exports.renderAccept = nativeBinding.renderAccept +module.exports.renderEmoji = nativeBinding.renderEmoji +module.exports.renderFlag = nativeBinding.renderFlag +module.exports.renderFollow = nativeBinding.renderFollow module.exports.renderFollowRelay = nativeBinding.renderFollowRelay +module.exports.renderHashtag = nativeBinding.renderHashtag +module.exports.renderMention = nativeBinding.renderMention +module.exports.renderRead = nativeBinding.renderRead +module.exports.renderTombstone = nativeBinding.renderTombstone module.exports.safeForSql = nativeBinding.safeForSql module.exports.sendPushNotification = nativeBinding.sendPushNotification module.exports.shouldNyaify = nativeBinding.shouldNyaify @@ -453,7 +462,6 @@ module.exports.unwatchNote = nativeBinding.unwatchNote module.exports.updateAntennaCache = nativeBinding.updateAntennaCache module.exports.updateAntennasOnNewNote = nativeBinding.updateAntennasOnNewNote module.exports.updateMetaCache = nativeBinding.updateMetaCache -module.exports.updateNodeinfoCache = nativeBinding.updateNodeinfoCache module.exports.UserEmojiModPerm = nativeBinding.UserEmojiModPerm module.exports.UserEvent = nativeBinding.UserEvent module.exports.UserProfileFfvisibility = nativeBinding.UserProfileFfvisibility diff --git a/packages/backend-rs/package.json b/packages/backend-rs/package.json index a33effccb0..dbc6aa9770 100644 --- a/packages/backend-rs/package.json +++ b/packages/backend-rs/package.json @@ -11,7 +11,8 @@ "@napi-rs/cli": "3.0.0-alpha.62" }, "scripts": { - "build": "napi build --features napi --no-const-enum --platform --release --output-dir ./built/", + "fetch": "cargo fetch --locked --manifest-path ../../Cargo.toml", + "build": "pnpm run fetch && napi build --features napi --no-const-enum --platform --release --output-dir ./built/ -- --frozen", "build:debug": "napi build --features napi --no-const-enum --platform --output-dir ./built/ --dts-header '/* auto-generated by NAPI-RS */\n/* Do NOT edit this file manually */\n\ntype DateTimeWithTimeZone = Date;\n\ntype Json = any;\n\n'" } } diff --git a/packages/backend-rs/src/cache/bare.rs b/packages/backend-rs/src/cache/bare.rs new file mode 100644 index 0000000000..a930d010ff --- /dev/null +++ b/packages/backend-rs/src/cache/bare.rs @@ -0,0 +1,199 @@ +//! In-memory cache handler + +use chrono::{DateTime, Duration, Utc}; +use std::sync::Mutex; + +/// Cache stored directly in memory +pub struct Cache { + cache: Mutex>, + ttl: Option, +} + +struct TimedData { + value: Option, + last_updated: DateTime, +} + +impl Default for Cache { + fn default() -> Self { + Self::new() + } +} + +impl Cache { + /// Creates a new cache object with no auto invalidation. + pub const fn new() -> Self { + Self { + cache: Mutex::new(TimedData { + value: None, + last_updated: DateTime::UNIX_EPOCH, + }), + ttl: None, + } + } + + /// Creates a new cache object whose content is invalidated + /// in the specified duration. + /// + /// # Example + /// ``` + /// # use backend_rs::cache::Cache; + /// use chrono::Duration; + /// static CACHE: Cache = Cache::new_with_ttl(Duration::seconds(1)); + /// + /// fn use_cache() { + /// let data = 998244353; + /// + /// // Set cache + /// CACHE.set(data); + /// + /// // wait for the cache to expire + /// std::thread::sleep(std::time::Duration::from_millis(1100)); + /// + /// // Get cache + /// let cache = CACHE.get(); + /// + /// assert!(cache.is_none()); + /// } + /// ``` + pub const fn new_with_ttl(ttl: Duration) -> Self { + Self { + cache: Mutex::new(TimedData { + value: None, + last_updated: DateTime::UNIX_EPOCH, + }), + ttl: Some(ttl), + } + } + + /// Sets a cache. This function overwrites the existing data. + /// + /// # Example + /// ``` + /// # use backend_rs::cache::Cache; + /// static CACHE: Cache = Cache::new(); + /// + /// fn use_cache() { + /// let data = 998244353; + /// + /// // Set cache + /// CACHE.set(data); + /// + /// // Get cache + /// let cache = CACHE.get(); + /// + /// if let Some(cached_data) = cache { + /// println!("found a cached value"); + /// assert_eq!(data, cached_data) + /// } else { + /// println!("cache not found"); + /// } + /// } + /// ``` + pub fn set(&self, value: T) { + if self.ttl.is_none() { + let mut cache = match self.cache.lock() { + Ok(cache) => cache, + Err(err) => err.into_inner(), + }; + cache.value = Some(value); + } else { + let mut cache = match self.cache.lock() { + Ok(cache) => cache, + Err(err) => err.into_inner(), + }; + + *cache = TimedData { + value: Some(value), + last_updated: Utc::now(), + }; + } + } + + /// Gets a cache. Returns [`None`] is the cache is not set or expired. + pub fn get(&self) -> Option { + let data = self.cache.lock().ok()?; + + if let Some(ttl) = self.ttl { + if data.last_updated + ttl < Utc::now() { + return None; + } + } + data.value.to_owned() + } +} + +#[cfg(test)] +mod unit_test { + use super::Cache; + use chrono::Duration; + use pretty_assertions::assert_eq; + + #[derive(Clone, Debug, PartialEq)] + struct Data { + id: u64, + name: String, + } + + #[test] + fn set_and_get() { + static CACHE: Cache = Cache::new(); + static CACHE_WITH_TTL: Cache = Cache::new_with_ttl(Duration::seconds(1)); + + let data = Data { + id: 16, + name: "Firefish".to_owned(), + }; + + assert!(CACHE.get().is_none()); + assert!(CACHE_WITH_TTL.get().is_none()); + + CACHE.set(data.clone()); + assert_eq!(data, CACHE.get().unwrap()); + + CACHE_WITH_TTL.set(data.clone()); + assert_eq!(data, CACHE_WITH_TTL.get().unwrap()); + } + + #[test] + fn expire() { + static CACHE: Cache = Cache::new_with_ttl(Duration::seconds(1)); + + let data = Data { + id: 16, + name: "Firefish".to_owned(), + }; + + CACHE.set(data); + + std::thread::sleep(std::time::Duration::from_millis(1100)); + + assert!(CACHE.get().is_none()); + } + + static GLOBAL_CACHE_1: Cache = Cache::new(); + static GLOBAL_CACHE_2: Cache = Cache::new_with_ttl(Duration::milliseconds(2)); + + #[tokio::test] + async fn use_cache_in_parallel() { + let mut tasks = Vec::new(); + + async fn f() -> Data { + Data { + id: rand::random(), + name: cuid2::create_id(), + } + } + + for _ in 0..20 { + tasks.push(tokio::spawn(async { + GLOBAL_CACHE_1.set(f().await); + GLOBAL_CACHE_2.set(f().await); + (GLOBAL_CACHE_1.get(), GLOBAL_CACHE_2.get()) + })) + } + for task in tasks { + task.await.unwrap(); + } + } +} diff --git a/packages/backend-rs/src/cache/mod.rs b/packages/backend-rs/src/cache/mod.rs new file mode 100644 index 0000000000..c68f689a4a --- /dev/null +++ b/packages/backend-rs/src/cache/mod.rs @@ -0,0 +1,7 @@ +//! Cache handlers + +pub mod bare; +pub mod redis; + +pub use bare::Cache; +pub use redis::{delete, delete_all, delete_one, get, get_one, set, set_one, Category}; diff --git a/packages/backend-rs/src/database/cache.rs b/packages/backend-rs/src/cache/redis.rs similarity index 86% rename from packages/backend-rs/src/database/cache.rs rename to packages/backend-rs/src/cache/redis.rs index d56da7f680..dfa5f3f19d 100644 --- a/packages/backend-rs/src/database/cache.rs +++ b/packages/backend-rs/src/cache/redis.rs @@ -1,6 +1,7 @@ //! Utilities for using Redis cache use crate::database::{redis_conn, redis_key, RedisConnError}; +use chrono::Duration; use redis::{AsyncCommands, RedisError}; use serde::{Deserialize, Serialize}; @@ -10,6 +11,7 @@ pub enum Category { Block, Follow, CatLang, + RandomIcon, #[cfg(test)] Test, } @@ -22,6 +24,8 @@ pub enum Error { RedisConn(#[from] RedisConnError), #[error("failed to encode data for Redis")] Encode(#[from] rmp_serde::encode::Error), + #[error("invalid cache TTL")] + TTL, } #[inline] @@ -35,6 +39,7 @@ fn categorize(category: Category, key: &str) -> String { Category::Block => "blocking", Category::Follow => "following", Category::CatLang => "catlang", + Category::RandomIcon => "randomIcon", #[cfg(test)] Category::Test => "usedOnlyForTesting", }; @@ -54,18 +59,19 @@ fn wildcard(category: Category) -> String { /// /// * `key` : key (prefixed automatically) /// * `value` : (de)serializable value -/// * `expire_seconds` : TTL +/// * `ttl` : cache lifetime /// /// # Example /// /// ``` -/// # use backend_rs::database::cache; +/// # use backend_rs::cache; +/// use chrono::Duration; /// # async fn f() -> Result<(), Box> { /// let key = "apple"; /// let data = "I want to cache this string".to_owned(); /// /// // caches the data for 10 seconds -/// cache::set(key, &data, 10).await?; +/// cache::set(key, &data, Duration::seconds(10)).await?; /// /// // get the cache /// let cached_data = cache::get::(key).await?; @@ -78,17 +84,16 @@ fn wildcard(category: Category) -> String { pub async fn set Deserialize<'a> + Serialize>( key: &str, value: &V, - expire_seconds: u64, + ttl: Duration, ) -> Result<(), Error> { - redis_conn() + Ok(redis_conn() .await? .set_ex( prefix_key(key), rmp_serde::encode::to_vec(&value)?, - expire_seconds, + ttl.num_seconds().try_into().map_err(|_| Error::TTL)?, ) - .await?; - Ok(()) + .await?) } /// Gets a Redis cache. @@ -103,13 +108,14 @@ pub async fn set Deserialize<'a> + Serialize>( /// # Example /// /// ``` -/// # use backend_rs::database::cache; +/// # use backend_rs::cache; +/// use chrono::Duration; /// # async fn f() -> Result<(), Box> { /// let key = "banana"; /// let data = "I want to cache this string".to_owned(); /// /// // set cache -/// cache::set(key, &data, 10).await?; +/// cache::set(key, &data, Duration::seconds(10)).await?; /// /// // get cache /// let cached_data = cache::get::(key).await?; @@ -142,13 +148,14 @@ pub async fn get Deserialize<'a> + Serialize>(key: &str) -> Result Result<(), Box> { /// let key = "chocolate"; /// let value = "I want to cache this string".to_owned(); /// /// // set cache -/// cache::set(key, &value, 10).await?; +/// cache::set(key, &value, Duration::seconds(10)).await?; /// /// // delete the cache /// cache::delete("foo").await?; @@ -174,14 +181,14 @@ pub async fn delete(key: &str) -> Result<(), Error> { /// * `category` : one of [Category] /// * `key` : key (prefixed automatically) /// * `value` : (de)serializable value -/// * `expire_seconds` : TTL +/// * `ttl` : cache lifetime pub async fn set_one Deserialize<'a> + Serialize>( category: Category, key: &str, value: &V, - expire_seconds: u64, + ttl: Duration, ) -> Result<(), Error> { - set(&categorize(category, key), value, expire_seconds).await + set(&categorize(category, key), value, ttl).await } /// Gets a Redis cache under a `category`. @@ -227,12 +234,11 @@ pub async fn delete_all(category: Category) -> Result<(), Error> { Ok(()) } -// TODO: get_all() - #[cfg(test)] mod unit_test { use super::{delete_all, get, get_one, set, set_one, Category::Test}; - use crate::database::cache::delete_one; + use crate::cache::delete_one; + use chrono::Duration; use pretty_assertions::assert_eq; #[tokio::test] @@ -256,9 +262,9 @@ mod unit_test { kind: "prime number".to_owned(), }; - set(key_1, &value_1, 1).await.unwrap(); - set(key_2, &value_2, 1).await.unwrap(); - set(key_3, &value_3, 1).await.unwrap(); + set(key_1, &value_1, Duration::seconds(1)).await.unwrap(); + set(key_2, &value_2, Duration::seconds(1)).await.unwrap(); + set(key_3, &value_3, Duration::seconds(1)).await.unwrap(); let cached_value_1: Vec = get(key_1).await.unwrap().unwrap(); let cached_value_2: String = get(key_2).await.unwrap().unwrap(); @@ -291,9 +297,15 @@ mod unit_test { let value_2 = 998244353u32; let value_3 = 'あ'; - 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(); + set_one(Test, key_1, &value_1, Duration::minutes(5)) + .await + .unwrap(); + set_one(Test, key_2, &value_2, Duration::minutes(5)) + .await + .unwrap(); + set_one(Test, key_3, &value_3, Duration::minutes(5)) + .await + .unwrap(); assert_eq!( get_one::(Test, key_1).await.unwrap().unwrap(), diff --git a/packages/backend-rs/src/config/meta.rs b/packages/backend-rs/src/config/meta.rs index 969d9d35f5..18e0c874da 100644 --- a/packages/backend-rs/src/config/meta.rs +++ b/packages/backend-rs/src/config/meta.rs @@ -1,31 +1,28 @@ //! Server information -use crate::{database::db_conn, model::entity::meta}; +use crate::{cache::Cache, database::db_conn, model::entity::meta}; +use chrono::Duration; use sea_orm::{prelude::*, ActiveValue}; -use std::sync::Mutex; type Meta = meta::Model; -static CACHE: Mutex> = Mutex::new(None); -fn set_cache(meta: &Meta) { - let _ = CACHE.lock().map(|mut cache| *cache = Some(meta.clone())); -} +static INSTANCE_META_CACHE: Cache = Cache::new_with_ttl(Duration::minutes(5)); #[macros::export(js_name = "fetchMeta")] pub async fn local_server_info() -> Result { - local_server_info_impl(true).await + local_server_info_impl(false).await } #[macros::export(js_name = "updateMetaCache")] pub async fn update() -> Result<(), DbErr> { - local_server_info_impl(false).await?; + local_server_info_impl(true).await?; Ok(()) } -async fn local_server_info_impl(use_cache: bool) -> Result { +async fn local_server_info_impl(force_update_cache: bool) -> Result { // try using cache - if use_cache { - if let Some(cache) = CACHE.lock().ok().and_then(|cache| cache.clone()) { + if !force_update_cache { + if let Some(cache) = INSTANCE_META_CACHE.get() { return Ok(cache); } } @@ -34,7 +31,7 @@ async fn local_server_info_impl(use_cache: bool) -> Result { let db = db_conn().await?; let meta = meta::Entity::find().one(db).await?; if let Some(meta) = meta { - set_cache(&meta); + INSTANCE_META_CACHE.set(meta.clone()); return Ok(meta); } @@ -45,7 +42,7 @@ async fn local_server_info_impl(use_cache: bool) -> Result { }) .exec_with_returning(db) .await?; - set_cache(&meta); + INSTANCE_META_CACHE.set(meta.clone()); Ok(meta) } diff --git a/packages/backend-rs/src/database/mod.rs b/packages/backend-rs/src/database/mod.rs index 03315c1080..68c2c60d88 100644 --- a/packages/backend-rs/src/database/mod.rs +++ b/packages/backend-rs/src/database/mod.rs @@ -6,6 +6,5 @@ pub use redis::get_conn as redis_conn; pub use redis::key as redis_key; pub use redis::RedisConnError; -pub mod cache; pub mod postgresql; pub mod redis; diff --git a/packages/backend-rs/src/federation/activitypub/object/accept.rs b/packages/backend-rs/src/federation/activitypub/object/accept.rs new file mode 100644 index 0000000000..e9147f4d09 --- /dev/null +++ b/packages/backend-rs/src/federation/activitypub/object/accept.rs @@ -0,0 +1,30 @@ +use super::*; +use crate::{config::CONFIG, misc::user}; +use uuid::Uuid; + +#[macros::export(object)] +pub struct ApAccept { + pub id: String, + pub r#type: ApObject, + pub actor: String, + pub object: follow::ApFollow, +} + +impl ActivityPubObject for ApAccept {} + +impl ApAccept { + #[allow(dead_code)] // TODO: remove this line + fn new(user_id: String, follow_object: follow::ApFollow) -> Self { + Self { + id: format!("{}/{}", CONFIG.url, Uuid::new_v4()), + r#type: ApObject::Accept, + actor: user::local_uri(user_id), + object: follow_object, + } + } +} + +#[macros::ts_export] +pub fn render_accept(user_id: String, follow_object: follow::ApFollow) -> ApAccept { + ApAccept::new(user_id, follow_object) +} diff --git a/packages/backend-rs/src/federation/activitypub/object/emoji.rs b/packages/backend-rs/src/federation/activitypub/object/emoji.rs new file mode 100644 index 0000000000..0c9107c7ce --- /dev/null +++ b/packages/backend-rs/src/federation/activitypub/object/emoji.rs @@ -0,0 +1,49 @@ +use super::*; +use crate::{misc, model::entity::emoji}; +use chrono::Utc; + +#[macros::export(object)] +pub struct ApEmoji { + pub id: String, + pub r#type: ApObject, + pub name: String, + pub updated: String, + pub icon: Icon, +} + +#[macros::export(object)] +pub struct Icon { + pub r#type: ApObject, + pub media_type: String, + pub url: String, +} + +impl ActivityPubObject for ApEmoji {} + +impl ApEmoji { + #[allow(dead_code)] // TODO: remove this line + fn new(emoji: emoji::Model) -> Self { + Self { + id: misc::emoji::local_uri(&emoji.name), + r#type: ApObject::Emoji, + name: format!(":{}:", emoji.name), + updated: emoji + .updated_at + .unwrap_or_else(|| Utc::now().into()) + .to_rfc3339(), + icon: Icon { + r#type: ApObject::Image, + media_type: emoji.r#type.unwrap_or_else(|| "image/png".to_owned()), + url: emoji.public_url, + }, + } + } +} + +#[macros::for_ts] // https://github.com/napi-rs/napi-rs/issues/2060 +type Emoji = emoji::Model; + +#[macros::ts_export] +pub fn render_emoji(emoji: Emoji) -> ApEmoji { + ApEmoji::new(emoji) +} diff --git a/packages/backend-rs/src/federation/activitypub/object/flag.rs b/packages/backend-rs/src/federation/activitypub/object/flag.rs new file mode 100644 index 0000000000..0ade189d30 --- /dev/null +++ b/packages/backend-rs/src/federation/activitypub/object/flag.rs @@ -0,0 +1,36 @@ +use super::*; +use crate::{federation::internal_actor, misc::user}; + +#[macros::export(object)] +pub struct ApFlag { + pub r#type: ApObject, + pub actor: String, + pub content: String, + // TODO: object can be an array of uri's + pub object: String, +} + +impl ActivityPubObject for ApFlag {} + +impl ApFlag { + #[allow(dead_code)] // TODO: remove this line + async fn new( + target_user_uri: String, + comment: String, + ) -> Result { + Ok(Self { + r#type: ApObject::Flag, + actor: user::local_uri(&internal_actor::instance::get().await?.id), + content: comment, + object: target_user_uri, + }) + } +} + +#[macros::ts_export] +pub async fn render_flag( + target_user_uri: String, + comment: String, +) -> Result { + ApFlag::new(target_user_uri, comment).await +} diff --git a/packages/backend-rs/src/federation/activitypub/object/follow.rs b/packages/backend-rs/src/federation/activitypub/object/follow.rs new file mode 100644 index 0000000000..f5954a9c55 --- /dev/null +++ b/packages/backend-rs/src/federation/activitypub/object/follow.rs @@ -0,0 +1,70 @@ +use super::*; +use crate::{config::CONFIG, federation::internal_actor, misc::user}; + +#[macros::export(object)] +pub struct ApFollow { + pub id: String, + pub r#type: ApObject, + pub actor: String, + pub object: String, +} + +impl ActivityPubObject for ApFollow {} + +#[macros::errors] +pub enum Error { + #[error("follower uri is missing")] + MissingFollowerUri, + #[error("followee uri is missing")] + MissingFolloweeUri, +} + +impl ApFollow { + #[allow(dead_code)] // TODO: remove this line + fn new( + follower: UserLike, + followee: UserLike, + request_id: Option, + ) -> Result { + Ok(Self { + id: request_id.unwrap_or_else(|| { + format!("{}/follows/{}/{}", CONFIG.url, follower.id, followee.id) + }), + r#type: ApObject::Follow, + actor: match user::is_local!(follower) { + true => user::local_uri(follower.id), + false => follower.uri.ok_or(Error::MissingFollowerUri)?, + }, + object: match user::is_local!(followee) { + true => user::local_uri(followee.id), + false => followee.uri.ok_or(Error::MissingFolloweeUri)?, + }, + }) + } + + #[allow(dead_code)] // TODO: remove this line + async fn new_relay(relay_id: String) -> Result { + Ok(Self { + id: format!("{}/activities/follow-relay/{}", CONFIG.url, relay_id), + r#type: ApObject::Follow, + actor: user::local_uri(internal_actor::relay::get_id().await?), + object: AS_PUBLIC_URL.to_owned(), + }) + } +} + +#[macros::ts_export] +pub fn render_follow( + follower: UserLike, + followee: UserLike, + request_id: Option, +) -> Result { + ApFollow::new(follower, followee, request_id) +} + +#[macros::ts_export] +pub async fn render_follow_relay( + relay_id: String, +) -> Result { + ApFollow::new_relay(relay_id).await +} diff --git a/packages/backend-rs/src/federation/activitypub/object/hashtag.rs b/packages/backend-rs/src/federation/activitypub/object/hashtag.rs new file mode 100644 index 0000000000..5c1fa12e53 --- /dev/null +++ b/packages/backend-rs/src/federation/activitypub/object/hashtag.rs @@ -0,0 +1,27 @@ +use super::*; +use crate::config::CONFIG; + +#[macros::export(object)] +pub struct ApHashtag { + pub id: String, + pub r#type: ApObject, + pub name: String, +} + +impl ActivityPubObject for ApHashtag {} + +impl ApHashtag { + #[allow(dead_code)] // TODO: remove this line + fn new(tag_name: &str) -> Self { + Self { + id: format!("{}/tags/{}", CONFIG.url, urlencoding::encode(tag_name)), + r#type: ApObject::Hashtag, + name: format!("#{}", tag_name), + } + } +} + +#[macros::ts_export] +pub fn render_hashtag(tag_name: &str) -> ApHashtag { + ApHashtag::new(tag_name) +} diff --git a/packages/backend-rs/src/federation/activitypub/object/mention.rs b/packages/backend-rs/src/federation/activitypub/object/mention.rs new file mode 100644 index 0000000000..effd069bae --- /dev/null +++ b/packages/backend-rs/src/federation/activitypub/object/mention.rs @@ -0,0 +1,40 @@ +use super::*; +use crate::{federation::acct::Acct, misc::user}; + +#[derive(thiserror::Error, Debug)] +#[error("remote user's uri is missing")] +pub struct MissingRemoteUserUri; + +#[macros::export(object)] +pub struct ApMention { + pub r#type: ApObject, + pub href: String, + pub name: String, +} + +impl ActivityPubObject for ApMention {} + +impl ApMention { + #[allow(dead_code)] // TODO: remove this line + fn new(user: UserLike) -> Result { + Ok(Self { + r#type: ApObject::Mention, + href: match user::is_local!(user) { + true => user::local_uri(user.id), + false => user.uri.ok_or(MissingRemoteUserUri)?, + }, + name: format!( + "@{}", + Acct { + username: user.username, + host: user.host + } + ), + }) + } +} + +#[macros::ts_export] +pub fn render_mention(user: UserLike) -> Result { + ApMention::new(user) +} diff --git a/packages/backend-rs/src/federation/activitypub/object/mod.rs b/packages/backend-rs/src/federation/activitypub/object/mod.rs index b245bca98a..7745031907 100644 --- a/packages/backend-rs/src/federation/activitypub/object/mod.rs +++ b/packages/backend-rs/src/federation/activitypub/object/mod.rs @@ -1,9 +1,33 @@ -pub mod relay; +pub mod accept; +pub mod emoji; +pub mod flag; +pub mod follow; +pub mod hashtag; +pub mod mention; +pub mod read; +pub mod tombstone; pub trait ActivityPubObject {} -#[derive(serde::Serialize)] #[macros::export(string_enum)] -pub enum Activity { +pub enum ApObject { + Accept, + Emoji, + Flag, Follow, + Hashtag, + Mention, + Image, + Read, + Tombstone, +} + +const AS_PUBLIC_URL: &str = "https://www.w3.org/ns/activitystreams#Public"; + +#[macros::export(object)] +pub struct UserLike { + pub id: String, + pub username: String, + pub host: Option, + pub uri: Option, } diff --git a/packages/backend-rs/src/federation/activitypub/object/read.rs b/packages/backend-rs/src/federation/activitypub/object/read.rs new file mode 100644 index 0000000000..8e69b9965e --- /dev/null +++ b/packages/backend-rs/src/federation/activitypub/object/read.rs @@ -0,0 +1,27 @@ +use super::*; +use crate::misc::user; + +#[macros::export(object)] +pub struct ApRead { + pub r#type: ApObject, + pub actor: String, + pub object: String, +} + +impl ActivityPubObject for ApRead {} + +impl ApRead { + #[allow(dead_code)] // TODO: remove this line + fn new(user_id: String, message_uri: String) -> Self { + Self { + r#type: ApObject::Read, + actor: user::local_uri(user_id), + object: message_uri, + } + } +} + +#[macros::ts_export] +pub fn render_read(user_id: String, message_uri: String) -> ApRead { + ApRead::new(user_id, message_uri) +} diff --git a/packages/backend-rs/src/federation/activitypub/object/relay.rs b/packages/backend-rs/src/federation/activitypub/object/relay.rs deleted file mode 100644 index f3d7656564..0000000000 --- a/packages/backend-rs/src/federation/activitypub/object/relay.rs +++ /dev/null @@ -1,28 +0,0 @@ -use super::*; -use crate::{config::CONFIG, federation::internal_actor}; -use serde::Serialize; - -#[derive(Serialize)] -#[macros::export(object)] -pub struct FollowRelay { - pub id: String, - pub r#type: Activity, - pub actor: String, - pub object: String, -} - -impl ActivityPubObject for FollowRelay {} - -#[macros::export(js_name = "renderFollowRelay")] -pub async fn follow(relay_id: &str) -> Result { - Ok(FollowRelay { - id: format!("{}/activities/follow-relay/{}", CONFIG.url, relay_id), - r#type: Activity::Follow, - actor: format!( - "{}/users/{}", - CONFIG.url, - internal_actor::relay::get_id().await? - ), - object: "https://www.w3.org/ns/activitystreams#Public".to_owned(), - }) -} diff --git a/packages/backend-rs/src/federation/activitypub/object/tombstone.rs b/packages/backend-rs/src/federation/activitypub/object/tombstone.rs new file mode 100644 index 0000000000..cf8069b475 --- /dev/null +++ b/packages/backend-rs/src/federation/activitypub/object/tombstone.rs @@ -0,0 +1,25 @@ +use super::*; +use crate::misc::note; + +#[macros::export(object)] +pub struct ApTombstone { + pub id: String, + pub r#type: ApObject, +} + +impl ActivityPubObject for ApTombstone {} + +impl ApTombstone { + #[allow(dead_code)] // TODO: remove this line + fn new(note_id: String) -> Self { + Self { + id: note::local_uri(note_id), + r#type: ApObject::Tombstone, + } + } +} + +#[macros::ts_export] +pub fn render_tombstone(note_id: String) -> ApTombstone { + ApTombstone::new(note_id) +} diff --git a/packages/backend-rs/src/federation/internal_actor/instance.rs b/packages/backend-rs/src/federation/internal_actor/instance.rs index 957aa57492..119ecee549 100644 --- a/packages/backend-rs/src/federation/internal_actor/instance.rs +++ b/packages/backend-rs/src/federation/internal_actor/instance.rs @@ -4,12 +4,8 @@ use crate::{database::db_conn, model::entity::user}; use sea_orm::prelude::*; use tokio::sync::OnceCell; -// for napi export -// https://github.com/napi-rs/napi-rs/issues/2060 -type User = user::Model; - pub const USERNAME: &str = "instance.actor"; -static INSTANCE_ACTOR: OnceCell = OnceCell::const_new(); +static INSTANCE_ACTOR: OnceCell = OnceCell::const_new(); #[macros::errors] pub enum Error { @@ -20,7 +16,7 @@ pub enum Error { Db(#[from] DbErr), } -async fn set_cache() -> Result<&'static User, Error> { +async fn set_cache() -> Result<&'static user::Model, Error> { let instance_actor = INSTANCE_ACTOR .get_or_try_init(|| async { tracing::debug!("caching @instance.actor"); @@ -37,13 +33,16 @@ async fn set_cache() -> Result<&'static User, Error> { Ok(instance_actor) } -pub async fn get() -> Result<&'static User, Error> { +pub async fn get() -> Result<&'static user::Model, Error> { match INSTANCE_ACTOR.get() { Some(model) => Ok(model), None => set_cache().await, } } +#[macros::for_ts] // https://github.com/napi-rs/napi-rs/issues/2060 +type User = user::Model; + #[macros::ts_export(js_name = "getInstanceActor")] pub async fn get_js() -> Result { Ok(get().await?.to_owned()) diff --git a/packages/backend-rs/src/federation/nodeinfo/generate.rs b/packages/backend-rs/src/federation/nodeinfo/generate.rs index 4ab7ef65fa..58baa7d64c 100644 --- a/packages/backend-rs/src/federation/nodeinfo/generate.rs +++ b/packages/backend-rs/src/federation/nodeinfo/generate.rs @@ -1,23 +1,19 @@ //! NodeInfo generator use crate::{ + cache::Cache, config::{local_server_info, CONFIG}, database::db_conn, federation::nodeinfo::schema::*, misc, model::entity::{note, user}, }; +use chrono::Duration; use sea_orm::prelude::*; use serde_json::json; -use std::{collections::HashMap, sync::Mutex}; +use std::collections::HashMap; -static CACHE: Mutex> = Mutex::new(None); - -fn set_cache(nodeinfo: &Nodeinfo21) { - let _ = CACHE - .lock() - .map(|mut cache| *cache = Some(nodeinfo.to_owned())); -} +static NODEINFO_CACHE: Cache = Cache::new_with_ttl(Duration::hours(1)); /// Fetches the number of total/active local users and local posts. /// @@ -127,32 +123,26 @@ async fn generate_nodeinfo_2_1() -> Result { }) } -async fn nodeinfo_2_1_impl(use_cache: bool) -> Result { - if use_cache { - if let Some(nodeinfo) = CACHE.lock().ok().and_then(|cache| cache.to_owned()) { - return Ok(nodeinfo); - } +/// Returns NodeInfo (version 2.1) of the local server. +pub async fn nodeinfo_2_1() -> Result { + if let Some(nodeinfo) = NODEINFO_CACHE.get() { + return Ok(nodeinfo); } let nodeinfo = generate_nodeinfo_2_1().await?; tracing::info!("updating cache"); - set_cache(&nodeinfo); + NODEINFO_CACHE.set(nodeinfo.clone()); Ok(nodeinfo) } -/// Returns NodeInfo (version 2.1) of the local server. -pub async fn nodeinfo_2_1() -> Result { - nodeinfo_2_1_impl(true).await -} - /// Returns NodeInfo (version 2.0) of the local server. pub async fn nodeinfo_2_0() -> Result { Ok(nodeinfo_2_1().await?.into()) } -#[cfg(any(test, doctest, feature = "napi"))] +#[macros::for_ts] #[macros::errors] pub enum Error { #[doc = "database error"] @@ -171,9 +161,3 @@ pub async fn nodeinfo_2_1_as_json() -> Result { pub async fn nodeinfo_2_0_as_json() -> Result { Ok(serde_json::to_value(nodeinfo_2_0().await?)?) } - -#[macros::ts_export(js_name = "updateNodeinfoCache")] -pub async fn update_cache() -> Result<(), DbErr> { - nodeinfo_2_1_impl(false).await?; - Ok(()) -} diff --git a/packages/backend-rs/src/init/system_info.rs b/packages/backend-rs/src/init/system_info.rs index 82505a1625..b49fb59ebc 100644 --- a/packages/backend-rs/src/init/system_info.rs +++ b/packages/backend-rs/src/init/system_info.rs @@ -1,28 +1,10 @@ -use std::sync::{Mutex, MutexGuard, OnceLock, PoisonError}; +use crate::misc::system_info; use sysinfo::System; -pub type SysinfoPoisonError = PoisonError>; - -static SYSTEM_INFO: OnceLock> = OnceLock::new(); - -/// Gives an access to the shared static [System] object. -/// -/// # Example -/// -/// ``` -/// # use backend_rs::init::system_info::{system_info, SysinfoPoisonError}; -/// let system_info = system_info().lock()?; -/// println!("The number of CPU threads is {}.", system_info.cpus().len()); -/// # Ok::<(), SysinfoPoisonError>(()) -/// ``` -pub fn system_info() -> &'static std::sync::Mutex { - SYSTEM_INFO.get_or_init(|| Mutex::new(System::new_all())) -} - /// Prints the server hardware information as the server info log. #[macros::export] -pub fn show_server_info() -> Result<(), SysinfoPoisonError> { - let system_info = system_info().lock()?; +pub fn show_server_info() { + let system_info = system_info::get(); tracing::info!( "Hostname: {}", @@ -45,6 +27,4 @@ pub fn show_server_info() -> Result<(), SysinfoPoisonError> { tracing::info!("Free memory: {} MiB", system_info.free_memory() / 1048576); tracing::info!("Total swap: {} MiB", system_info.total_swap() / 1048576); tracing::info!("Free swap: {} MiB", system_info.free_swap() / 1048576); - - Ok(()) } diff --git a/packages/backend-rs/src/lib.rs b/packages/backend-rs/src/lib.rs index ff7a49a471..89d357ed77 100644 --- a/packages/backend-rs/src/lib.rs +++ b/packages/backend-rs/src/lib.rs @@ -1,5 +1,6 @@ #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))] +pub mod cache; pub mod config; pub mod database; pub mod federation; diff --git a/packages/backend-rs/src/misc/emoji/mod.rs b/packages/backend-rs/src/misc/emoji/mod.rs new file mode 100644 index 0000000000..bb89f3d4ca --- /dev/null +++ b/packages/backend-rs/src/misc/emoji/mod.rs @@ -0,0 +1,9 @@ +pub mod reaction; +pub mod unicode; + +use crate::config::CONFIG; + +/// Returns URI of a local custom emoji. +pub fn local_uri(emoji_code: impl std::fmt::Display) -> String { + format!("{}/emojis/{}", CONFIG.url, emoji_code) +} diff --git a/packages/backend-rs/src/misc/reaction.rs b/packages/backend-rs/src/misc/emoji/reaction.rs similarity index 100% rename from packages/backend-rs/src/misc/reaction.rs rename to packages/backend-rs/src/misc/emoji/reaction.rs diff --git a/packages/backend-rs/src/misc/emoji.rs b/packages/backend-rs/src/misc/emoji/unicode.rs similarity index 100% rename from packages/backend-rs/src/misc/emoji.rs rename to packages/backend-rs/src/misc/emoji/unicode.rs diff --git a/packages/backend-rs/src/misc/get_image_size.rs b/packages/backend-rs/src/misc/get_image_size.rs index d10151483a..89498e46ed 100644 --- a/packages/backend-rs/src/misc/get_image_size.rs +++ b/packages/backend-rs/src/misc/get_image_size.rs @@ -1,7 +1,8 @@ -use crate::{database::cache, util::http_client}; +use crate::{cache, util::http_client}; +use chrono::Duration; use futures_util::AsyncReadExt; use image::{ImageError, ImageFormat, ImageReader}; -use isahc::prelude::*; +use isahc::AsyncReadResponseExt; use nom_exif::{parse_jpeg_exif, EntryValue, ExifTag}; use std::io::Cursor; use tokio::sync::Mutex; @@ -9,7 +10,7 @@ use tokio::sync::Mutex; #[macros::errors] pub enum Error { #[error("Redis cache operation has failed")] - Cache(#[from] cache::Error), + Cache(#[from] cache::redis::Error), #[error("failed to acquire an HTTP client")] HttpClient(#[from] http_client::Error), #[error("HTTP request failed")] @@ -63,7 +64,7 @@ pub async fn get_image_size_from_url(url: &str) -> Result { .is_some(); if !attempted { - cache::set_one(cache::Category::FetchUrl, url, &true, 10 * 60).await?; + cache::set_one(cache::Category::FetchUrl, url, &true, Duration::minutes(10)).await?; } } @@ -134,7 +135,7 @@ pub async fn get_image_size_from_url(url: &str) -> Result { #[cfg(test)] mod unit_test { use super::ImageSize; - use crate::database::cache; + use crate::cache; use pretty_assertions::assert_eq; #[tokio::test] diff --git a/packages/backend-rs/src/misc/latest_version.rs b/packages/backend-rs/src/misc/latest_version.rs index a7e5568427..5df10de9ee 100644 --- a/packages/backend-rs/src/misc/latest_version.rs +++ b/packages/backend-rs/src/misc/latest_version.rs @@ -1,14 +1,13 @@ //! Fetch latest Firefish version from the Firefish repository -use crate::{database::cache, util::http_client}; +use crate::{cache::Cache, util::http_client}; +use chrono::Duration; use futures_util::AsyncReadExt; use isahc::AsyncReadResponseExt; use serde::Deserialize; #[macros::errors] pub enum Error { - #[error("Redis cache operation has failed")] - Cache(#[from] cache::Error), #[error("HTTP request failed")] Isahc(#[from] isahc::Error), #[error("failed to acquire an HTTP client")] @@ -22,15 +21,17 @@ pub enum Error { Json(#[from] serde_json::Error), } +#[derive(Clone, Deserialize)] +struct PackageJson { + version: String, +} + const UPSTREAM_PACKAGE_JSON_URL: &str = "https://firefish.dev/firefish/firefish/-/raw/main/package.json"; -async fn get_latest_version() -> Result { - #[derive(Debug, Deserialize)] - struct Response { - version: String, - } +static PACKAGE_JSON_CACHE: Cache = Cache::new_with_ttl(Duration::hours(3)); +async fn get_package_json() -> Result { // Read up to 1 MiB of the response body let mut response = http_client::client()? .get_async(UPSTREAM_PACKAGE_JSON_URL) @@ -42,42 +43,33 @@ async fn get_latest_version() -> Result { return Err(Error::BadStatus(response.status().to_string())); } - let res_parsed: Response = serde_json::from_str(&response.text().await?)?; + let package_json: PackageJson = serde_json::from_str(&response.text().await?)?; - Ok(res_parsed.version) + Ok(package_json) } /// Returns the latest Firefish version. #[macros::export] pub async fn latest_version() -> Result { - let version: Option = - cache::get_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL).await?; - - if let Some(v) = version { - tracing::trace!("use cached value: {}", v); - Ok(v) + if let Some(package_json) = PACKAGE_JSON_CACHE.get() { + tracing::trace!("use cached value: {}", package_json.version); + Ok(package_json.version) } else { tracing::trace!("cache is expired, fetching the latest version"); - let fetched_version = get_latest_version().await?; - tracing::trace!("fetched value: {}", fetched_version); + let package_json = get_package_json().await?; + tracing::trace!("fetched value: {}", package_json.version); - cache::set_one( - cache::Category::FetchUrl, - UPSTREAM_PACKAGE_JSON_URL, - &fetched_version, - 3 * 60 * 60, - ) - .await?; - Ok(fetched_version) + PACKAGE_JSON_CACHE.set(package_json.clone()); + Ok(package_json.version) } } #[cfg(test)] mod unit_test { - use super::{latest_version, UPSTREAM_PACKAGE_JSON_URL}; - use crate::database::cache; + use super::latest_version; + use pretty_assertions::assert_eq; - fn validate_version(version: String) { + fn validate_version(version: &str) { // version: YYYYMMDD or YYYYMMDD-X assert!(version.len() >= 8); assert!(version[..8].chars().all(|c| c.is_ascii_digit())); @@ -103,15 +95,14 @@ mod unit_test { #[tokio::test] #[cfg_attr(miri, ignore)] // can't call foreign function `getaddrinfo` on OS `linux` async fn get_latest_version() { - // delete caches in case you run this test multiple times - cache::delete_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL) - .await - .unwrap(); - // fetch from firefish.dev - validate_version(latest_version().await.unwrap()); + let version_1 = latest_version().await.unwrap(); + validate_version(&version_1); // use cache - validate_version(latest_version().await.unwrap()); + let version_2 = latest_version().await.unwrap(); + validate_version(&version_2); + + assert_eq!(version_1, version_2); } } diff --git a/packages/backend-rs/src/misc/mod.rs b/packages/backend-rs/src/misc/mod.rs index 4c715f965c..64bfae025c 100644 --- a/packages/backend-rs/src/misc/mod.rs +++ b/packages/backend-rs/src/misc/mod.rs @@ -13,7 +13,7 @@ pub mod latest_version; pub mod note; pub mod nyaify; pub mod password; -pub mod reaction; +pub mod random_icon; pub mod remove_old_attestation_challenges; pub mod should_nyaify; pub mod system_info; diff --git a/packages/backend-rs/src/misc/note/mod.rs b/packages/backend-rs/src/misc/note/mod.rs index 84e2560af8..366bcaa197 100644 --- a/packages/backend-rs/src/misc/note/mod.rs +++ b/packages/backend-rs/src/misc/note/mod.rs @@ -3,3 +3,10 @@ pub use summarize::summarize; pub mod elaborate; pub mod summarize; + +use crate::config::CONFIG; + +/// Returns URI of a local post. +pub fn local_uri(note_id: impl std::fmt::Display) -> String { + format!("{}/notes/{}", CONFIG.url, note_id) +} diff --git a/packages/backend-rs/src/misc/random_icon.rs b/packages/backend-rs/src/misc/random_icon.rs new file mode 100644 index 0000000000..c5463ebc18 --- /dev/null +++ b/packages/backend-rs/src/misc/random_icon.rs @@ -0,0 +1,43 @@ +use crate::cache; +use chrono::Duration; +use identicon_rs::{error::IdenticonError, Identicon}; + +#[macros::errors] +pub enum Error { + #[doc = "failed to generate identicon"] + #[error(transparent)] + Identicon(#[from] IdenticonError), + #[error("Redis cache operation has failed")] + Cache(#[from] cache::redis::Error), +} + +pub async fn generate(id: &str) -> Result, Error> { + if let Some(icon) = cache::get_one::>(cache::Category::RandomIcon, id).await? { + Ok(icon) + } else { + let icon = Identicon::new(id) + .set_border(16) + .set_scale(96)? + .export_png_data()?; + cache::set_one( + cache::Category::RandomIcon, + id, + &icon, + Duration::minutes(10), + ) + .await?; + Ok(icon) + } +} + +#[cfg(feature = "napi")] +#[napi_derive::napi(js_name = "genIdenticon")] +pub async fn generate_js(id: String) -> napi::Result { + match generate(&id).await { + Ok(icon) => Ok(icon.into()), + Err(err) => Err(napi::Error::from_reason(format!( + "\n{}\n", + crate::util::error_chain::format_error(&err) + ))), + } +} diff --git a/packages/backend-rs/src/misc/should_nyaify.rs b/packages/backend-rs/src/misc/should_nyaify.rs index 8476678c77..66e54f5996 100644 --- a/packages/backend-rs/src/misc/should_nyaify.rs +++ b/packages/backend-rs/src/misc/should_nyaify.rs @@ -1,9 +1,7 @@ //! Determine whether to enable the cat language conversion -use crate::{ - database::{cache, db_conn}, - model::entity::user, -}; +use crate::{cache, database::db_conn, model::entity::user}; +use chrono::Duration; use sea_orm::{DbErr, EntityTrait, QuerySelect, SelectColumns}; #[macros::errors] @@ -13,7 +11,8 @@ pub enum Error { Db(#[from] DbErr), #[doc = "cache error"] #[error(transparent)] - Cache(#[from] cache::Error), + Cache(#[from] cache::redis::Error), + #[doc = "user not found"] #[error("user {0} not found")] NotFound(String), } @@ -37,7 +36,7 @@ pub async fn should_nyaify(reader_user_id: &str) -> Result { cache::Category::CatLang, reader_user_id, &fetched_value, - 10 * 60, + Duration::minutes(10), ) .await?; diff --git a/packages/backend-rs/src/misc/system_info.rs b/packages/backend-rs/src/misc/system_info.rs index d5191c16f6..bb8d537814 100644 --- a/packages/backend-rs/src/misc/system_info.rs +++ b/packages/backend-rs/src/misc/system_info.rs @@ -1,10 +1,35 @@ //! Utilities to check hardware information such as cpu, memory, storage usage - -use crate::init::system_info::{system_info, SysinfoPoisonError}; -use sysinfo::{Disks, MemoryRefreshKind}; - // TODO: i64 -> u64 (we can't export u64 to Node.js) +use std::sync::{Mutex, OnceLock}; +use sysinfo::{Disks, MemoryRefreshKind, System}; + +static SYSTEM_INFO: OnceLock> = OnceLock::new(); + +/// Gives you access to the shared static [System] object. +/// +/// # Example +/// +/// ``` +/// # use backend_rs::misc::system_info; +/// let system_info = system_info::get(); +/// println!("The number of CPU threads is {}.", system_info.cpus().len()); +/// println!("The total memory is {} MiB.", system_info.total_memory() / 1048576); +/// ``` +pub fn get() -> std::sync::MutexGuard<'static, System> { + let guard = SYSTEM_INFO + .get_or_init(|| Mutex::new(System::new_all())) + .lock(); + + if let Err(err) = guard { + let mut inner = err.into_inner(); + *inner = System::new_all(); + inner + } else { + guard.unwrap() + } +} + #[macros::export(object)] pub struct Cpu { pub model: String, @@ -31,10 +56,10 @@ pub struct Storage { } #[macros::export] -pub fn cpu_info() -> Result { - let system_info = system_info().lock()?; +pub fn cpu_info() -> Cpu { + let system_info = get(); - Ok(Cpu { + Cpu { model: match system_info.cpus() { [] => { tracing::debug!("failed to get CPU info"); @@ -43,31 +68,31 @@ pub fn cpu_info() -> Result { cpus => cpus[0].brand().to_owned(), }, cores: system_info.cpus().len() as u16, - }) + } } #[macros::export] -pub fn cpu_usage() -> Result { - let mut system_info = system_info().lock()?; +pub fn cpu_usage() -> f32 { + let mut system_info = get(); system_info.refresh_cpu_usage(); let total_cpu_usage: f32 = system_info.cpus().iter().map(|cpu| cpu.cpu_usage()).sum(); let cpu_threads = system_info.cpus().len(); - Ok(total_cpu_usage / (cpu_threads as f32)) + total_cpu_usage / (cpu_threads as f32) } #[macros::export] -pub fn memory_usage() -> Result { - let mut system_info = system_info().lock()?; +pub fn memory_usage() -> Memory { + let mut system_info = get(); system_info.refresh_memory_specifics(MemoryRefreshKind::new().with_ram()); - Ok(Memory { + Memory { total: system_info.total_memory() as i64, used: system_info.used_memory() as i64, available: system_info.available_memory() as i64, - }) + } } #[macros::export] diff --git a/packages/backend-rs/src/misc/user/mod.rs b/packages/backend-rs/src/misc/user/mod.rs index 16ee43aeca..9272084ad0 100644 --- a/packages/backend-rs/src/misc/user/mod.rs +++ b/packages/backend-rs/src/misc/user/mod.rs @@ -1 +1,30 @@ pub mod count; + +use crate::config::CONFIG; + +/// Returns URI of a local user. +pub fn local_uri(user_id: impl std::fmt::Display) -> String { + format!("{}/users/{}", CONFIG.url, user_id) +} + +#[doc(hidden)] // hide the macro in the top doc page +#[macro_export] +macro_rules! is_local { + ($user_like:expr) => { + $user_like.host.is_none() + }; +} + +#[doc(inline)] // show the macro in the module doc page +pub use is_local; + +#[doc(hidden)] // hide the macro in the top doc page +#[macro_export] +macro_rules! is_remote { + ($user_like:expr) => { + $user_like.host.is_some() + }; +} + +#[doc(inline)] // show the macro in the module doc page +pub use is_remote; 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 70ab552eaa..d18af43f8f 100644 --- a/packages/backend-rs/src/model/entity/abuse_user_report.rs +++ b/packages/backend-rs/src/model/entity/abuse_user_report.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/access_token.rs b/packages/backend-rs/src/model/entity/access_token.rs index 5895217842..7b82c302f4 100644 --- a/packages/backend-rs/src/model/entity/access_token.rs +++ b/packages/backend-rs/src/model/entity/access_token.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/ad.rs b/packages/backend-rs/src/model/entity/ad.rs index 54495037d4..a245ced127 100644 --- a/packages/backend-rs/src/model/entity/ad.rs +++ b/packages/backend-rs/src/model/entity/ad.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/announcement.rs b/packages/backend-rs/src/model/entity/announcement.rs index 750c8d17a7..3a02f6c38b 100644 --- a/packages/backend-rs/src/model/entity/announcement.rs +++ b/packages/backend-rs/src/model/entity/announcement.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/announcement_read.rs b/packages/backend-rs/src/model/entity/announcement_read.rs index 169b6c9bd1..18100a9925 100644 --- a/packages/backend-rs/src/model/entity/announcement_read.rs +++ b/packages/backend-rs/src/model/entity/announcement_read.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/antenna.rs b/packages/backend-rs/src/model/entity/antenna.rs index 45d40f10b7..796e240f2d 100644 --- a/packages/backend-rs/src/model/entity/antenna.rs +++ b/packages/backend-rs/src/model/entity/antenna.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use super::sea_orm_active_enums::AntennaSrc; use sea_orm::entity::prelude::*; diff --git a/packages/backend-rs/src/model/entity/app.rs b/packages/backend-rs/src/model/entity/app.rs index 06c1d50817..5664b75e8f 100644 --- a/packages/backend-rs/src/model/entity/app.rs +++ b/packages/backend-rs/src/model/entity/app.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/attestation_challenge.rs b/packages/backend-rs/src/model/entity/attestation_challenge.rs index ca3804ac04..f81c99634a 100644 --- a/packages/backend-rs/src/model/entity/attestation_challenge.rs +++ b/packages/backend-rs/src/model/entity/attestation_challenge.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/auth_session.rs b/packages/backend-rs/src/model/entity/auth_session.rs index 1dc6acd414..981ff5eb0b 100644 --- a/packages/backend-rs/src/model/entity/auth_session.rs +++ b/packages/backend-rs/src/model/entity/auth_session.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/blocking.rs b/packages/backend-rs/src/model/entity/blocking.rs index 3a9a0753d1..c6a3ee154d 100644 --- a/packages/backend-rs/src/model/entity/blocking.rs +++ b/packages/backend-rs/src/model/entity/blocking.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/channel.rs b/packages/backend-rs/src/model/entity/channel.rs index 221a72bc4c..cc1f99881e 100644 --- a/packages/backend-rs/src/model/entity/channel.rs +++ b/packages/backend-rs/src/model/entity/channel.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/channel_following.rs b/packages/backend-rs/src/model/entity/channel_following.rs index 9be21b8511..4113c1e362 100644 --- a/packages/backend-rs/src/model/entity/channel_following.rs +++ b/packages/backend-rs/src/model/entity/channel_following.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; 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 d03ee961dd..002f5357ff 100644 --- a/packages/backend-rs/src/model/entity/channel_note_pining.rs +++ b/packages/backend-rs/src/model/entity/channel_note_pining.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/clip.rs b/packages/backend-rs/src/model/entity/clip.rs index fcd5b11315..a9428bfe70 100644 --- a/packages/backend-rs/src/model/entity/clip.rs +++ b/packages/backend-rs/src/model/entity/clip.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/clip_note.rs b/packages/backend-rs/src/model/entity/clip_note.rs index 9cce432242..17ad84b825 100644 --- a/packages/backend-rs/src/model/entity/clip_note.rs +++ b/packages/backend-rs/src/model/entity/clip_note.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/drive_file.rs b/packages/backend-rs/src/model/entity/drive_file.rs index e2139a9a7d..4ec086ee51 100644 --- a/packages/backend-rs/src/model/entity/drive_file.rs +++ b/packages/backend-rs/src/model/entity/drive_file.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use super::sea_orm_active_enums::DriveFileUsageHint; use sea_orm::entity::prelude::*; @@ -31,11 +31,11 @@ pub struct Model { pub thumbnail_url: Option, #[sea_orm(column_name = "webpublicUrl")] pub webpublic_url: Option, - #[sea_orm(column_name = "accessKey")] + #[sea_orm(column_name = "accessKey", unique)] pub access_key: Option, - #[sea_orm(column_name = "thumbnailAccessKey")] + #[sea_orm(column_name = "thumbnailAccessKey", unique)] pub thumbnail_access_key: Option, - #[sea_orm(column_name = "webpublicAccessKey")] + #[sea_orm(column_name = "webpublicAccessKey", unique)] pub webpublic_access_key: Option, pub uri: Option, pub src: Option, diff --git a/packages/backend-rs/src/model/entity/drive_folder.rs b/packages/backend-rs/src/model/entity/drive_folder.rs index 428fc70251..757be7cd3f 100644 --- a/packages/backend-rs/src/model/entity/drive_folder.rs +++ b/packages/backend-rs/src/model/entity/drive_folder.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/emoji.rs b/packages/backend-rs/src/model/entity/emoji.rs index 44744ede08..3a058c1913 100644 --- a/packages/backend-rs/src/model/entity/emoji.rs +++ b/packages/backend-rs/src/model/entity/emoji.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/follow_request.rs b/packages/backend-rs/src/model/entity/follow_request.rs index f7b6231242..fec79c6a93 100644 --- a/packages/backend-rs/src/model/entity/follow_request.rs +++ b/packages/backend-rs/src/model/entity/follow_request.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/following.rs b/packages/backend-rs/src/model/entity/following.rs index 3b4459bd9e..874fe8e142 100644 --- a/packages/backend-rs/src/model/entity/following.rs +++ b/packages/backend-rs/src/model/entity/following.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/gallery_like.rs b/packages/backend-rs/src/model/entity/gallery_like.rs index 062854ef29..eda880ed27 100644 --- a/packages/backend-rs/src/model/entity/gallery_like.rs +++ b/packages/backend-rs/src/model/entity/gallery_like.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/gallery_post.rs b/packages/backend-rs/src/model/entity/gallery_post.rs index 3fc93da1ee..b5c008a959 100644 --- a/packages/backend-rs/src/model/entity/gallery_post.rs +++ b/packages/backend-rs/src/model/entity/gallery_post.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/hashtag.rs b/packages/backend-rs/src/model/entity/hashtag.rs index f9bb74b4fe..28a23b8c78 100644 --- a/packages/backend-rs/src/model/entity/hashtag.rs +++ b/packages/backend-rs/src/model/entity/hashtag.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, + #[sea_orm(unique)] pub name: String, #[sea_orm(column_name = "mentionedUserIds")] pub mentioned_user_ids: Vec, diff --git a/packages/backend-rs/src/model/entity/instance.rs b/packages/backend-rs/src/model/entity/instance.rs index 9a49e25ce0..4711ffec3d 100644 --- a/packages/backend-rs/src/model/entity/instance.rs +++ b/packages/backend-rs/src/model/entity/instance.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; @@ -12,6 +12,7 @@ pub struct Model { pub id: String, #[sea_orm(column_name = "caughtAt")] pub caught_at: DateTimeWithTimeZone, + #[sea_orm(unique)] pub host: String, #[sea_orm(column_name = "usersCount")] pub users_count: i32, diff --git a/packages/backend-rs/src/model/entity/messaging_message.rs b/packages/backend-rs/src/model/entity/messaging_message.rs index 202496f012..0b84db722e 100644 --- a/packages/backend-rs/src/model/entity/messaging_message.rs +++ b/packages/backend-rs/src/model/entity/messaging_message.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/meta.rs b/packages/backend-rs/src/model/entity/meta.rs index 023fcef525..6d6fd4a61e 100644 --- a/packages/backend-rs/src/model/entity/meta.rs +++ b/packages/backend-rs/src/model/entity/meta.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/migrations.rs b/packages/backend-rs/src/model/entity/migrations.rs index b03aaabc3f..f597ee4373 100644 --- a/packages/backend-rs/src/model/entity/migrations.rs +++ b/packages/backend-rs/src/model/entity/migrations.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/mod.rs b/packages/backend-rs/src/model/entity/mod.rs index ffb21352d2..ae55b9bd91 100644 --- a/packages/backend-rs/src/model/entity/mod.rs +++ b/packages/backend-rs/src/model/entity/mod.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 pub mod prelude; diff --git a/packages/backend-rs/src/model/entity/moderation_log.rs b/packages/backend-rs/src/model/entity/moderation_log.rs index 2ce71a9c78..43a40dfa50 100644 --- a/packages/backend-rs/src/model/entity/moderation_log.rs +++ b/packages/backend-rs/src/model/entity/moderation_log.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/muted_note.rs b/packages/backend-rs/src/model/entity/muted_note.rs index 5e89c2ed2f..9b441fcaab 100644 --- a/packages/backend-rs/src/model/entity/muted_note.rs +++ b/packages/backend-rs/src/model/entity/muted_note.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use super::sea_orm_active_enums::MutedNoteReason; use sea_orm::entity::prelude::*; diff --git a/packages/backend-rs/src/model/entity/muting.rs b/packages/backend-rs/src/model/entity/muting.rs index eff07c6a17..15dd8d3e69 100644 --- a/packages/backend-rs/src/model/entity/muting.rs +++ b/packages/backend-rs/src/model/entity/muting.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/note.rs b/packages/backend-rs/src/model/entity/note.rs index e3bf3c9490..66cd80a34f 100644 --- a/packages/backend-rs/src/model/entity/note.rs +++ b/packages/backend-rs/src/model/entity/note.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use super::sea_orm_active_enums::NoteVisibility; use sea_orm::entity::prelude::*; @@ -33,6 +33,7 @@ pub struct Model { #[sea_orm(column_type = "JsonBinary")] pub reactions: Json, pub visibility: NoteVisibility, + #[sea_orm(unique)] pub uri: Option, pub score: i32, #[sea_orm(column_name = "fileIds")] diff --git a/packages/backend-rs/src/model/entity/note_edit.rs b/packages/backend-rs/src/model/entity/note_edit.rs index 66e9a8e5ca..985b4bd4fc 100644 --- a/packages/backend-rs/src/model/entity/note_edit.rs +++ b/packages/backend-rs/src/model/entity/note_edit.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/note_favorite.rs b/packages/backend-rs/src/model/entity/note_favorite.rs index 8db8612109..7bdede2dcb 100644 --- a/packages/backend-rs/src/model/entity/note_favorite.rs +++ b/packages/backend-rs/src/model/entity/note_favorite.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/note_file.rs b/packages/backend-rs/src/model/entity/note_file.rs index 6742bf67aa..12ce7483ff 100644 --- a/packages/backend-rs/src/model/entity/note_file.rs +++ b/packages/backend-rs/src/model/entity/note_file.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/note_reaction.rs b/packages/backend-rs/src/model/entity/note_reaction.rs index e329db8d0f..970017b0e8 100644 --- a/packages/backend-rs/src/model/entity/note_reaction.rs +++ b/packages/backend-rs/src/model/entity/note_reaction.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; 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 29aa782bd5..8d0e7779c0 100644 --- a/packages/backend-rs/src/model/entity/note_thread_muting.rs +++ b/packages/backend-rs/src/model/entity/note_thread_muting.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/note_unread.rs b/packages/backend-rs/src/model/entity/note_unread.rs index da312e6e69..3f1588007d 100644 --- a/packages/backend-rs/src/model/entity/note_unread.rs +++ b/packages/backend-rs/src/model/entity/note_unread.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/note_watching.rs b/packages/backend-rs/src/model/entity/note_watching.rs index 5ab675a8e3..174dfb60b6 100644 --- a/packages/backend-rs/src/model/entity/note_watching.rs +++ b/packages/backend-rs/src/model/entity/note_watching.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/notification.rs b/packages/backend-rs/src/model/entity/notification.rs index e73a9cd1fb..7c4cdfa7c7 100644 --- a/packages/backend-rs/src/model/entity/notification.rs +++ b/packages/backend-rs/src/model/entity/notification.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use super::sea_orm_active_enums::NotificationType; use sea_orm::entity::prelude::*; diff --git a/packages/backend-rs/src/model/entity/page.rs b/packages/backend-rs/src/model/entity/page.rs index f252a43532..1bfdf539ad 100644 --- a/packages/backend-rs/src/model/entity/page.rs +++ b/packages/backend-rs/src/model/entity/page.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use super::sea_orm_active_enums::PageVisibility; use sea_orm::entity::prelude::*; diff --git a/packages/backend-rs/src/model/entity/page_like.rs b/packages/backend-rs/src/model/entity/page_like.rs index d6aa22c12a..4f39d8018c 100644 --- a/packages/backend-rs/src/model/entity/page_like.rs +++ b/packages/backend-rs/src/model/entity/page_like.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; 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 bd4978a44c..f097b1a691 100644 --- a/packages/backend-rs/src/model/entity/password_reset_request.rs +++ b/packages/backend-rs/src/model/entity/password_reset_request.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; @@ -12,6 +12,7 @@ pub struct Model { pub id: String, #[sea_orm(column_name = "createdAt")] pub created_at: DateTimeWithTimeZone, + #[sea_orm(unique)] 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 501243252f..c150ca1044 100644 --- a/packages/backend-rs/src/model/entity/poll.rs +++ b/packages/backend-rs/src/model/entity/poll.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use super::sea_orm_active_enums::PollNoteVisibility; use sea_orm::entity::prelude::*; diff --git a/packages/backend-rs/src/model/entity/poll_vote.rs b/packages/backend-rs/src/model/entity/poll_vote.rs index f3a84d2cdf..83c42b8304 100644 --- a/packages/backend-rs/src/model/entity/poll_vote.rs +++ b/packages/backend-rs/src/model/entity/poll_vote.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/prelude.rs b/packages/backend-rs/src/model/entity/prelude.rs index 57fff023db..00ffe2e547 100644 --- a/packages/backend-rs/src/model/entity/prelude.rs +++ b/packages/backend-rs/src/model/entity/prelude.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 pub use super::abuse_user_report::Entity as AbuseUserReport; pub use super::access_token::Entity as AccessToken; diff --git a/packages/backend-rs/src/model/entity/promo_note.rs b/packages/backend-rs/src/model/entity/promo_note.rs index 24b1b98651..bb4726d14d 100644 --- a/packages/backend-rs/src/model/entity/promo_note.rs +++ b/packages/backend-rs/src/model/entity/promo_note.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/promo_read.rs b/packages/backend-rs/src/model/entity/promo_read.rs index 097b6e3cc4..ee17eee88e 100644 --- a/packages/backend-rs/src/model/entity/promo_read.rs +++ b/packages/backend-rs/src/model/entity/promo_read.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/registration_ticket.rs b/packages/backend-rs/src/model/entity/registration_ticket.rs index 7a25c016b8..69e882899a 100644 --- a/packages/backend-rs/src/model/entity/registration_ticket.rs +++ b/packages/backend-rs/src/model/entity/registration_ticket.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; @@ -12,6 +12,7 @@ pub struct Model { pub id: String, #[sea_orm(column_name = "createdAt")] pub created_at: DateTimeWithTimeZone, + #[sea_orm(unique)] 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 7dbc5e37b9..c6be92557f 100644 --- a/packages/backend-rs/src/model/entity/registry_item.rs +++ b/packages/backend-rs/src/model/entity/registry_item.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/relay.rs b/packages/backend-rs/src/model/entity/relay.rs index 6648179f87..57ea86502a 100644 --- a/packages/backend-rs/src/model/entity/relay.rs +++ b/packages/backend-rs/src/model/entity/relay.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use super::sea_orm_active_enums::RelayStatus; use sea_orm::entity::prelude::*; @@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize}; pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, + #[sea_orm(unique)] pub inbox: String, pub status: RelayStatus, } diff --git a/packages/backend-rs/src/model/entity/renote_muting.rs b/packages/backend-rs/src/model/entity/renote_muting.rs index 1adc965f16..2da55c5002 100644 --- a/packages/backend-rs/src/model/entity/renote_muting.rs +++ b/packages/backend-rs/src/model/entity/renote_muting.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/reply_muting.rs b/packages/backend-rs/src/model/entity/reply_muting.rs index 5f01f223df..acd7727d5a 100644 --- a/packages/backend-rs/src/model/entity/reply_muting.rs +++ b/packages/backend-rs/src/model/entity/reply_muting.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs b/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs index 4cdbccf29f..672ba3cd56 100644 --- a/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs +++ b/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/signin.rs b/packages/backend-rs/src/model/entity/signin.rs index aec9f2dfe6..89f18a09f2 100644 --- a/packages/backend-rs/src/model/entity/signin.rs +++ b/packages/backend-rs/src/model/entity/signin.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/sw_subscription.rs b/packages/backend-rs/src/model/entity/sw_subscription.rs index c6757814a0..0a67b1515a 100644 --- a/packages/backend-rs/src/model/entity/sw_subscription.rs +++ b/packages/backend-rs/src/model/entity/sw_subscription.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use super::sea_orm_active_enums::PushSubscriptionType; use sea_orm::entity::prelude::*; diff --git a/packages/backend-rs/src/model/entity/used_username.rs b/packages/backend-rs/src/model/entity/used_username.rs index 84e55baba7..61bd6ab26a 100644 --- a/packages/backend-rs/src/model/entity/used_username.rs +++ b/packages/backend-rs/src/model/entity/used_username.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/user.rs b/packages/backend-rs/src/model/entity/user.rs index f13dc4c9af..38b2736b90 100644 --- a/packages/backend-rs/src/model/entity/user.rs +++ b/packages/backend-rs/src/model/entity/user.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use super::sea_orm_active_enums::UserEmojiModPerm; use sea_orm::entity::prelude::*; diff --git a/packages/backend-rs/src/model/entity/user_group.rs b/packages/backend-rs/src/model/entity/user_group.rs index f035750e4b..c97536ebc2 100644 --- a/packages/backend-rs/src/model/entity/user_group.rs +++ b/packages/backend-rs/src/model/entity/user_group.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; 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 41073b38fc..fc69b36b80 100644 --- a/packages/backend-rs/src/model/entity/user_group_invitation.rs +++ b/packages/backend-rs/src/model/entity/user_group_invitation.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; 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 28c831155c..467ec6af81 100644 --- a/packages/backend-rs/src/model/entity/user_group_invite.rs +++ b/packages/backend-rs/src/model/entity/user_group_invite.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; 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 99f38d1856..bbed05bbd0 100644 --- a/packages/backend-rs/src/model/entity/user_group_joining.rs +++ b/packages/backend-rs/src/model/entity/user_group_joining.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/user_ip.rs b/packages/backend-rs/src/model/entity/user_ip.rs index 326d82b74a..1e6e0931b5 100644 --- a/packages/backend-rs/src/model/entity/user_ip.rs +++ b/packages/backend-rs/src/model/entity/user_ip.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/user_keypair.rs b/packages/backend-rs/src/model/entity/user_keypair.rs index cbc203e6ec..6b47e5bae0 100644 --- a/packages/backend-rs/src/model/entity/user_keypair.rs +++ b/packages/backend-rs/src/model/entity/user_keypair.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/user_list.rs b/packages/backend-rs/src/model/entity/user_list.rs index 839eab1c02..061ddb1628 100644 --- a/packages/backend-rs/src/model/entity/user_list.rs +++ b/packages/backend-rs/src/model/entity/user_list.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; 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 3158cbad21..c7d9ddb3cf 100644 --- a/packages/backend-rs/src/model/entity/user_list_joining.rs +++ b/packages/backend-rs/src/model/entity/user_list_joining.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; 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 2a4c4e83e2..4139e78910 100644 --- a/packages/backend-rs/src/model/entity/user_note_pining.rs +++ b/packages/backend-rs/src/model/entity/user_note_pining.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/user_pending.rs b/packages/backend-rs/src/model/entity/user_pending.rs index 6c0265bc2f..5f731a981f 100644 --- a/packages/backend-rs/src/model/entity/user_pending.rs +++ b/packages/backend-rs/src/model/entity/user_pending.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; @@ -12,6 +12,7 @@ pub struct Model { pub id: String, #[sea_orm(column_name = "createdAt")] pub created_at: DateTimeWithTimeZone, + #[sea_orm(unique)] 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 afebc6f0d7..e33e242b19 100644 --- a/packages/backend-rs/src/model/entity/user_profile.rs +++ b/packages/backend-rs/src/model/entity/user_profile.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use super::sea_orm_active_enums::UserProfileFfvisibility; use super::sea_orm_active_enums::UserProfileMutingNotificationTypes; diff --git a/packages/backend-rs/src/model/entity/user_publickey.rs b/packages/backend-rs/src/model/entity/user_publickey.rs index a0605e9d2e..0b00e66179 100644 --- a/packages/backend-rs/src/model/entity/user_publickey.rs +++ b/packages/backend-rs/src/model/entity/user_publickey.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; pub struct Model { #[sea_orm(column_name = "userId", primary_key, auto_increment = false, unique)] pub user_id: String, - #[sea_orm(column_name = "keyId")] + #[sea_orm(column_name = "keyId", unique)] pub key_id: String, #[sea_orm(column_name = "keyPem")] pub key_pem: 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 fdc61638a3..1b4884917c 100644 --- a/packages/backend-rs/src/model/entity/user_security_key.rs +++ b/packages/backend-rs/src/model/entity/user_security_key.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/model/entity/webhook.rs b/packages/backend-rs/src/model/entity/webhook.rs index b2695bee62..943146a330 100644 --- a/packages/backend-rs/src/model/entity/webhook.rs +++ b/packages/backend-rs/src/model/entity/webhook.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/packages/backend-rs/src/service/antenna/cache.rs b/packages/backend-rs/src/service/antenna/cache.rs deleted file mode 100644 index ce83895368..0000000000 --- a/packages/backend-rs/src/service/antenna/cache.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! In-memory antennas cache handler - -use crate::{database::db_conn, model::entity::antenna}; -use sea_orm::prelude::*; -use std::sync::{Arc, Mutex}; - -static CACHE: Mutex>> = Mutex::new(None); - -fn set(antennas: Arc<[antenna::Model]>) { - let _ = CACHE.lock().map(|mut cache| *cache = Some(antennas)); -} - -pub(super) async fn update() -> Result, DbErr> { - tracing::debug!("updating cache"); - let antennas: Arc<[antenna::Model]> = - antenna::Entity::find().all(db_conn().await?).await?.into(); - set(antennas.clone()); - Ok(antennas) -} - -pub(super) async fn get() -> Result, DbErr> { - if let Some(cache) = CACHE.lock().ok().and_then(|cache| cache.clone()) { - return Ok(cache); - } - update().await -} diff --git a/packages/backend-rs/src/service/antenna/check_hit.rs b/packages/backend-rs/src/service/antenna/check_hit.rs index 0ef8f13595..87ed46ee70 100644 --- a/packages/backend-rs/src/service/antenna/check_hit.rs +++ b/packages/backend-rs/src/service/antenna/check_hit.rs @@ -1,9 +1,11 @@ use crate::{ + cache, config::CONFIG, - database::{cache, db_conn}, + database::db_conn, federation::acct::Acct, model::entity::{antenna, blocking, following, note, sea_orm_active_enums::*}, }; +use chrono::Duration; use sea_orm::{prelude::*, QuerySelect}; #[macros::errors] @@ -12,7 +14,7 @@ pub enum AntennaCheckError { #[error(transparent)] Db(#[from] DbErr), #[error("Redis cache operation has failed")] - Cache(#[from] cache::Error), + Cache(#[from] cache::redis::Error), } fn match_all(space_separated_words: &str, text: &str, case_sensitive: bool) -> bool { @@ -108,7 +110,13 @@ pub(super) async fn check_hit_antenna( .into_tuple::() .all(db) .await?; - cache::set_one(cache::Category::Block, ¬e.user_id, &blocks, 10 * 60).await?; + cache::set_one( + cache::Category::Block, + ¬e.user_id, + &blocks, + Duration::minutes(10), + ) + .await?; blocks }; @@ -137,7 +145,7 @@ pub(super) async fn check_hit_antenna( cache::Category::Follow, &antenna.user_id, &following, - 10 * 60, + Duration::minutes(10), ) .await?; following diff --git a/packages/backend-rs/src/service/antenna/mod.rs b/packages/backend-rs/src/service/antenna/mod.rs index 2f255581ed..975b1a66d8 100644 --- a/packages/backend-rs/src/service/antenna/mod.rs +++ b/packages/backend-rs/src/service/antenna/mod.rs @@ -1,4 +1,24 @@ -mod cache; mod check_hit; pub mod process_new_note; pub mod update; + +use crate::{cache::Cache, database::db_conn, model::entity::antenna}; +use sea_orm::prelude::*; +use std::sync::Arc; + +static ANTENNAS_CACHE: Cache> = Cache::new(); + +async fn update() -> Result, DbErr> { + tracing::debug!("updating cache"); + let antennas: Arc<[antenna::Model]> = + antenna::Entity::find().all(db_conn().await?).await?.into(); + ANTENNAS_CACHE.set(antennas.clone()); + Ok(antennas) +} + +async fn get_antennas() -> Result, DbErr> { + if let Some(cache) = ANTENNAS_CACHE.get() { + return Ok(cache); + } + update().await +} diff --git a/packages/backend-rs/src/service/antenna/process_new_note.rs b/packages/backend-rs/src/service/antenna/process_new_note.rs index d9099f867f..00afa1b4f3 100644 --- a/packages/backend-rs/src/service/antenna/process_new_note.rs +++ b/packages/backend-rs/src/service/antenna/process_new_note.rs @@ -1,5 +1,6 @@ use crate::{ - database::{cache, redis_conn, redis_key, RedisConnError}, + cache, + database::{redis_conn, redis_key, RedisConnError}, federation::acct::Acct, misc::note::elaborate, model::entity::note, @@ -19,7 +20,7 @@ pub enum Error { #[error(transparent)] Db(#[from] DbErr), #[error("Redis cache operation has failed")] - Cache(#[from] cache::Error), + Cache(#[from] cache::redis::Error), #[error("failed to execute a Redis command")] Redis(#[from] RedisError), #[error("bad Redis connection")] @@ -46,7 +47,7 @@ pub async fn update_antennas_on_new_note( let note_all_texts = elaborate!(note, false).await?; // TODO: do this in parallel - for antenna in antenna::cache::get().await?.iter() { + for antenna in antenna::get_antennas().await?.iter() { if note_muted_users.contains(&antenna.user_id) { continue; } @@ -59,8 +60,11 @@ pub async fn update_antennas_on_new_note( } async fn add_note_to_antenna(antenna_id: &str, note: &Note) -> Result<(), Error> { + // for streaming API + stream::antenna::publish(antenna_id.to_owned(), note).await?; + // for timeline API - redis_conn() + Ok(redis_conn() .await? .xadd_maxlen( redis_key(format!("antennaTimeline:{}", antenna_id)), @@ -68,10 +72,5 @@ async fn add_note_to_antenna(antenna_id: &str, note: &Note) -> Result<(), Error> format!("{}-*", get_timestamp(¬e.id)?), &[("note", ¬e.id)], ) - .await?; - - // for streaming API - stream::antenna::publish(antenna_id.to_owned(), note).await?; - - Ok(()) + .await?) } diff --git a/packages/backend-rs/src/service/antenna/update.rs b/packages/backend-rs/src/service/antenna/update.rs index 169e574379..d08ec2c968 100644 --- a/packages/backend-rs/src/service/antenna/update.rs +++ b/packages/backend-rs/src/service/antenna/update.rs @@ -2,6 +2,6 @@ #[macros::ts_export] pub async fn update_antenna_cache() -> Result<(), sea_orm::DbErr> { - super::cache::update().await?; + super::update().await?; Ok(()) } diff --git a/packages/backend-rs/src/service/stream.rs b/packages/backend-rs/src/service/stream.rs index bd412b26a9..4c8f05b332 100644 --- a/packages/backend-rs/src/service/stream.rs +++ b/packages/backend-rs/src/service/stream.rs @@ -112,13 +112,11 @@ pub async fn publish_to_stream( value.ok_or(Error::InvalidContent)? }; - redis_conn() + Ok(redis_conn() .await? .publish( &CONFIG.host, format!("{{\"channel\":\"{}\",\"message\":{}}}", channel, message), ) - .await?; - - Ok(()) + .await?) } diff --git a/packages/backend-rs/src/util/id.rs b/packages/backend-rs/src/util/id.rs index 4cce5897f0..0c580b3c5d 100644 --- a/packages/backend-rs/src/util/id.rs +++ b/packages/backend-rs/src/util/id.rs @@ -3,11 +3,10 @@ use crate::config::CONFIG; use basen::BASE36; use chrono::{DateTime, NaiveDateTime, Utc}; -use once_cell::sync::OnceCell; -use std::cmp; +use std::{cmp, sync::OnceLock}; -static FINGERPRINT: OnceCell = OnceCell::new(); -static GENERATOR: OnceCell = OnceCell::new(); +static FINGERPRINT: OnceLock = OnceLock::new(); +static GENERATOR: OnceLock = OnceLock::new(); const TIME_2000: i64 = 946_684_800_000; const TIMESTAMP_LENGTH: u8 = 8; diff --git a/packages/backend/package.json b/packages/backend/package.json index d0f89a4f2b..da84411cfe 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -13,33 +13,33 @@ "build": "pnpm tsc --project tsconfig.json ; pnpm tsc-alias --project tsconfig.json", "build:debug": "pnpm tsc --sourceMap --project tsconfig.json ; pnpm tsc-alias --project tsconfig.json", "watch": "pnpm tsc --project tsconfig.json --watch ; pnpm tsc-alias --project tsconfig.json --watch", - "lint": "pnpm biome check --write **/*.ts", - "format": "pnpm biome format * --write" + "lint": "pnpm biome check --write src", + "format": "pnpm biome format --write src" }, "dependencies": { - "@bull-board/api": "5.21.1", - "@bull-board/koa": "5.21.1", - "@bull-board/ui": "5.21.1", + "@bull-board/api": "5.21.3", + "@bull-board/koa": "5.21.3", + "@bull-board/ui": "5.21.3", "@discordapp/twemoji": "15.0.3", "@koa/cors": "5.0.0", "@koa/multer": "3.0.2", "@koa/router": "12.0.1", "@ladjs/koa-views": "9.0.0", "@peertube/http-signature": "1.7.0", - "@redocly/openapi-core": "1.18.1", + "@redocly/openapi-core": "1.19.0", "@sinonjs/fake-timers": "11.2.2", - "adm-zip": "0.5.14", + "adm-zip": "0.5.15", "ajv": "8.17.1", "archiver": "7.0.1", "async-lock": "1.4.1", "async-mutex": "0.5.0", - "aws-sdk": "2.1664.0", - "axios": "1.7.2", + "aws-sdk": "2.1671.0", + "axios": "1.7.3", "backend-rs": "workspace:*", "blurhash": "2.0.5", - "bull": "4.15.1", + "bull": "4.16.0", "cacheable-lookup": "git+https://github.com/TheEssem/cacheable-lookup.git#dd2fb616366a3c68dcf321a57a67295967b204bf", - "cbor-x": "1.5.9", + "cbor-x": "1.6.0", "chalk": "5.3.0", "cli-highlight": "2.1.11", "color-convert": "2.0.1", @@ -49,7 +49,7 @@ "deep-email-validator": "0.1.21", "escape-regexp": "0.0.1", "feed": "4.2.2", - "file-type": "19.3.0", + "file-type": "19.4.0", "firefish-js": "workspace:*", "fluent-ffmpeg": "2.1.3", "form-data": "4.0.0", @@ -58,7 +58,7 @@ "hpagent": "1.2.0", "ioredis": "5.4.1", "ip-cidr": "4.0.2", - "is-svg": "5.0.1", + "is-svg": "5.1.0", "jsdom": "24.1.1", "json5": "2.2.3", "jsonld": "8.3.2", @@ -87,10 +87,8 @@ "probe-image-size": "7.2.3", "promise-limit": "2.7.0", "punycode": "2.3.1", - "pureimage": "0.4.13", - "qrcode": "1.5.3", - "qs": "6.12.3", - "random-seed": "0.3.0", + "qrcode": "1.5.4", + "qs": "6.13.0", "ratelimiter": "3.4.1", "redis-semaphore": "5.6.0", "reflect-metadata": "0.2.2", @@ -120,7 +118,7 @@ "@types/color-convert": "2.0.3", "@types/content-disposition": "0.5.8", "@types/escape-regexp": "0.0.3", - "@types/fluent-ffmpeg": "2.1.24", + "@types/fluent-ffmpeg": "2.1.25", "@types/jsdom": "21.1.7", "@types/jsonld": "1.5.15", "@types/jsrsasign": "10.5.14", @@ -135,7 +133,7 @@ "@types/koa__cors": "5.0.0", "@types/koa__multer": "2.0.7", "@types/koa__router": "12.0.4", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/node-fetch": "2.6.11", "@types/nodemailer": "6.4.15", "@types/oauth": "0.9.5", @@ -157,7 +155,7 @@ "@types/tmp": "0.2.6", "@types/uuid": "10.0.0", "@types/websocket": "1.0.10", - "@types/ws": "8.5.11", + "@types/ws": "8.5.12", "cross-env": "7.0.3", "pug": "3.0.3", "strict-event-emitter-types": "2.0.0", @@ -165,7 +163,7 @@ "ts-node": "10.9.2", "tsc-alias": "1.8.10", "tsconfig-paths": "4.2.0", - "type-fest": "4.23.0", + "type-fest": "4.24.0", "typescript": "5.5.4", "webpack": "5.93.0", "ws": "8.18.0" diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index 801d2cf5fa..aae9cc2111 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -7,8 +7,6 @@ import { greet, removeOldAttestationChallenges, showServerInfo, - updateMetaCache, - updateNodeinfoCache, type Config, } from "backend-rs"; import { config } from "@/config.js"; @@ -29,7 +27,6 @@ export async function masterMain() { showEnvironment(); showNodejsVersion(); await connectDb(); - await updateMetaCache(); } catch (e) { bootLogger.error( `Fatal error occurred during initialization:\n${inspect(e)}`, @@ -51,10 +48,6 @@ export async function masterMain() { import("../daemons/server-stats.js").then((x) => x.default()); import("../daemons/queue-stats.js").then((x) => x.default()); - // Update meta cache every 5 minitues - setInterval(() => updateMetaCache(), 1000 * 60 * 5); - // Update nodeinfo cache every hour - setInterval(() => updateNodeinfoCache(), 1000 * 60 * 60); // Remove old attestation challenges setInterval(() => removeOldAttestationChallenges(), 1000 * 60 * 30); } @@ -77,7 +70,7 @@ function showNodejsVersion(): void { nodejsLogger.info(`Version ${process.version} detected.`); - const minVersion = "v18.19.0"; + const minVersion = "v18.20.0"; if (semver.lt(process.version, minVersion)) { nodejsLogger.error(`At least Node.js ${minVersion} required!`); process.exit(1); diff --git a/packages/backend/src/migration/1644010796173-convert-hard-mutes.ts b/packages/backend/src/migration/1644010796173-convert-hard-mutes.ts index ccadea6332..a2de6781de 100644 --- a/packages/backend/src/migration/1644010796173-convert-hard-mutes.ts +++ b/packages/backend/src/migration/1644010796173-convert-hard-mutes.ts @@ -2,11 +2,11 @@ import type { MigrationInterface, QueryRunner } from "typeorm"; export class convertHardMutes1644010796173 implements MigrationInterface { async up(queryRunner: QueryRunner): Promise { - let entries = await queryRunner.query( + const entries = await queryRunner.query( `SELECT "userId", "mutedWords" FROM "user_profile" WHERE "userHost" IS NULL`, ); for (let i = 0; i < entries.length; i++) { - let words = entries[i].mutedWords + const words = entries[i].mutedWords .map((line) => { if (typeof line === "string") return []; const regexp = line.join(" ").match(/^\/(.+)\/(.*)$/); @@ -39,11 +39,11 @@ export class convertHardMutes1644010796173 implements MigrationInterface { } async down(queryRunner: QueryRunner): Promise { - let entries = await queryRunner.query( + const entries = await queryRunner.query( `SELECT "userId", "mutedWords" FROM "user_profile"`, ); for (let i = 0; i < entries.length; i++) { - let words = entries[i].mutedWords + const words = entries[i].mutedWords .map((line) => { if (Array.isArray(line)) { return line; diff --git a/packages/backend/src/migration/1652859567549-uniform-themecolor.ts b/packages/backend/src/migration/1652859567549-uniform-themecolor.ts index 3d15b6ed51..0c62254fbf 100644 --- a/packages/backend/src/migration/1652859567549-uniform-themecolor.ts +++ b/packages/backend/src/migration/1652859567549-uniform-themecolor.ts @@ -5,7 +5,7 @@ import tinycolor from "tinycolor2"; export class uniformThemecolor1652859567549 implements MigrationInterface { async up(queryRunner: QueryRunner): Promise { const formatColor = (color) => { - let tc = new tinycolor(color); + const tc = new tinycolor(color); if (tc.isValid()) { return tc.toHexString(); } else { diff --git a/packages/backend/src/migration/1722346019160-set-emoji-public-url.ts b/packages/backend/src/migration/1722346019160-set-emoji-public-url.ts new file mode 100644 index 0000000000..8f3c95ccb9 --- /dev/null +++ b/packages/backend/src/migration/1722346019160-set-emoji-public-url.ts @@ -0,0 +1,18 @@ +import type { MigrationInterface, QueryRunner } from "typeorm"; + +export class SetEmojiPublicUrl1722346019160 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `UPDATE "emoji" SET "publicUrl" = "originalUrl" WHERE "publicUrl" = ''`, + ); + await queryRunner.query( + `ALTER TABLE "emoji" ALTER COLUMN "publicUrl" DROP DEFAULT`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "emoji" ALTER COLUMN "publicUrl" SET DEFAULT ''`, + ); + } +} diff --git a/packages/backend/src/misc/gen-identicon.ts b/packages/backend/src/misc/gen-identicon.ts deleted file mode 100644 index 1e51dfe2ac..0000000000 --- a/packages/backend/src/misc/gen-identicon.ts +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Identicon generator - * https://en.wikipedia.org/wiki/Identicon - */ - -import type { WriteStream } from "node:fs"; -import * as p from "pureimage"; -import gen from "random-seed"; - -const size = 128; // px -const n = 5; // resolution -const margin = size / 4; -const colors = [ - ["#eb6f92", "#b4637a"], - ["#f6c177", "#ea9d34"], - ["#ebbcba", "#d7827e"], - ["#9ccfd8", "#56949f"], - ["#c4a7e7", "#907aa9"], - ["#eb6f92", "#f6c177"], - ["#eb6f92", "#ebbcba"], - ["#eb6f92", "#31748f"], - ["#eb6f92", "#9ccfd8"], - ["#eb6f92", "#c4a7e7"], - ["#f6c177", "#eb6f92"], - ["#f6c177", "#ebbcba"], - ["#f6c177", "#31748f"], - ["#f6c177", "#9ccfd8"], - ["#f6c177", "#c4a7e7"], - ["#ebbcba", "#eb6f92"], - ["#ebbcba", "#f6c177"], - ["#ebbcba", "#31748f"], - ["#ebbcba", "#9ccfd8"], - ["#ebbcba", "#c4a7e7"], - ["#31748f", "#eb6f92"], - ["#31748f", "#f6c177"], - ["#31748f", "#ebbcba"], - ["#31748f", "#9ccfd8"], - ["#31748f", "#c4a7e7"], - ["#9ccfd8", "#eb6f92"], - ["#9ccfd8", "#f6c177"], - ["#9ccfd8", "#ebbcba"], - ["#9ccfd8", "#31748f"], - ["#9ccfd8", "#c4a7e7"], - ["#c4a7e7", "#eb6f92"], - ["#c4a7e7", "#f6c177"], - ["#c4a7e7", "#ebbcba"], - ["#c4a7e7", "#31748f"], - ["#c4a7e7", "#9ccfd8"], -]; - -const actualSize = size - margin * 2; -const cellSize = actualSize / n; -const sideN = Math.floor(n / 2); - -/** - * Generate buffer of an identicon by seed - */ -export function genIdenticon(seed: string, stream: WriteStream): Promise { - const rand = gen.create(seed); - const canvas = p.make(size, size, undefined); - const ctx = canvas.getContext("2d"); - - const bgColors = colors[rand(colors.length)]; - - const bg = ctx.createLinearGradient(0, 0, size, size); - bg.addColorStop(0, bgColors[0]); - bg.addColorStop(1, bgColors[1]); - - ctx.fillStyle = bg; - ctx.beginPath(); - ctx.fillRect(0, 0, size, size); - - ctx.fillStyle = "#ffffff"; - - // side bitmap (filled by false) - const side: boolean[][] = new Array(sideN); - for (let i = 0; i < side.length; i++) { - side[i] = new Array(n).fill(false); - } - - // 1*n (filled by false) - const center: boolean[] = new Array(n).fill(false); - - for (let x = 0; x < side.length; x++) { - for (let y = 0; y < side[x].length; y++) { - side[x][y] = rand(3) === 0; - } - } - - for (let i = 0; i < center.length; i++) { - center[i] = rand(3) === 0; - } - - // Draw - for (let x = 0; x < n; x++) { - for (let y = 0; y < n; y++) { - const isXCenter = x === (n - 1) / 2; - if (isXCenter && !center[y]) continue; - - const isLeftSide = x < (n - 1) / 2; - if (isLeftSide && !side[x][y]) continue; - - const isRightSide = x > (n - 1) / 2; - if (isRightSide && !side[sideN - (x - sideN)][y]) continue; - - const actualX = margin + cellSize * x; - const actualY = margin + cellSize * y; - ctx.beginPath(); - ctx.fillRect(actualX, actualY, cellSize, cellSize); - } - } - - return p.encodePNGToStream(canvas, stream); -} diff --git a/packages/backend/src/models/entities/emoji.ts b/packages/backend/src/models/entities/emoji.ts index 87b525dc59..0e5dcc32c9 100644 --- a/packages/backend/src/models/entities/emoji.ts +++ b/packages/backend/src/models/entities/emoji.ts @@ -38,7 +38,6 @@ export class Emoji { @Column("varchar", { length: 512, - default: "", }) public publicUrl: string; diff --git a/packages/backend/src/models/entities/note.ts b/packages/backend/src/models/entities/note.ts index 000177745c..43e289c057 100644 --- a/packages/backend/src/models/entities/note.ts +++ b/packages/backend/src/models/entities/note.ts @@ -138,7 +138,6 @@ export class Note { @Column("integer", { default: 0, - select: false, }) public score: number; diff --git a/packages/backend/src/models/entities/user-profile.ts b/packages/backend/src/models/entities/user-profile.ts index f74e04d2c3..3d514db1cd 100644 --- a/packages/backend/src/models/entities/user-profile.ts +++ b/packages/backend/src/models/entities/user-profile.ts @@ -195,7 +195,6 @@ export class UserProfile { @Index() @Column("boolean", { default: false, - select: false, }) public enableWordMute: boolean; diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index e71e04ef8d..3a6bd9602f 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -56,7 +56,6 @@ export class User { @Index() @Column("varchar", { length: 128, - select: false, comment: "The username (lowercased) of the User.", }) public usernameLower: string; diff --git a/packages/backend/src/models/repositories/renote-muting.ts b/packages/backend/src/models/repositories/renote-muting.ts index 18fd343a05..156d1a9b39 100644 --- a/packages/backend/src/models/repositories/renote-muting.ts +++ b/packages/backend/src/models/repositories/renote-muting.ts @@ -1,7 +1,7 @@ import { db } from "@/db/postgre.js"; -import { Packed } from "@/misc/schema.js"; +import type { Packed } from "@/misc/schema.js"; import { RenoteMuting } from "@/models/entities/renote-muting.js"; -import { User } from "@/models/entities/user.js"; +import type { User } from "@/models/entities/user.js"; import { awaitAll } from "@/prelude/await-all.js"; import { Users } from "../index.js"; diff --git a/packages/backend/src/models/repositories/reply-muting.ts b/packages/backend/src/models/repositories/reply-muting.ts index 948dc7d06f..a446941e1b 100644 --- a/packages/backend/src/models/repositories/reply-muting.ts +++ b/packages/backend/src/models/repositories/reply-muting.ts @@ -1,7 +1,7 @@ import { db } from "@/db/postgre.js"; -import { Packed } from "@/misc/schema.js"; +import type { Packed } from "@/misc/schema.js"; import { ReplyMuting } from "@/models/entities/reply-muting.js"; -import { User } from "@/models/entities/user.js"; +import type { User } from "@/models/entities/user.js"; import { awaitAll } from "@/prelude/await-all.js"; import { Users } from "../index.js"; diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index c04fb9750c..f64c067687 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -471,6 +471,7 @@ export const UserRepository = db.getRepository(User).extend({ .fetch( user.host, () => Instances.findOneBy({ host: user.host! }), + false, (v) => v != null, ) .then((instance) => diff --git a/packages/backend/src/queue/processors/db/import-firefish-post.ts b/packages/backend/src/queue/processors/db/import-firefish-post.ts index d0044819ec..08d5331807 100644 --- a/packages/backend/src/queue/processors/db/import-firefish-post.ts +++ b/packages/backend/src/queue/processors/db/import-firefish-post.ts @@ -39,7 +39,7 @@ export async function importCkPost( */ const urls = (post.files || []) .map((x: any) => x.url) - .filter((x: String) => x.startsWith("http")); + .filter((x: string) => x.startsWith("http")); const files: DriveFile[] = []; for (const url of urls) { try { diff --git a/packages/backend/src/queue/processors/db/import-masto-post.ts b/packages/backend/src/queue/processors/db/import-masto-post.ts index 4bf351e8ec..31749a198f 100644 --- a/packages/backend/src/queue/processors/db/import-masto-post.ts +++ b/packages/backend/src/queue/processors/db/import-masto-post.ts @@ -68,7 +68,7 @@ export async function importMastoPost( if (!isRenote && files.length == 0) { const urls = post.object.attachment .map((x: any) => x.url) - .filter((x: String) => x.startsWith("http")); + .filter((x: string) => x.startsWith("http")); files = []; for (const url of urls) { try { diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index e9c1b5bd1b..663f9be299 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -207,7 +207,7 @@ export async function createPerson( try { const data = await fetch(person.followers, { headers: { Accept: "application/json" }, - size: 1024 * 1024 + size: 1024 * 1024, }); const json_data = JSON.parse(await data.text()); @@ -223,7 +223,7 @@ export async function createPerson( try { const data = await fetch(person.following, { headers: { Accept: "application/json" }, - size: 1024 * 1024 + size: 1024 * 1024, }); const json_data = JSON.parse(await data.text()); @@ -492,7 +492,7 @@ export async function updatePerson( try { const data = await fetch(person.followers, { headers: { Accept: "application/json" }, - size: 1024 * 1024 + size: 1024 * 1024, }); const json_data = JSON.parse(await data.text()); @@ -508,7 +508,7 @@ export async function updatePerson( try { const data = await fetch(person.following, { headers: { Accept: "application/json" }, - size: 1024 * 1024 + size: 1024 * 1024, }); const json_data = JSON.parse(await data.text()); diff --git a/packages/backend/src/remote/activitypub/renderer/accept.ts b/packages/backend/src/remote/activitypub/renderer/accept.ts deleted file mode 100644 index 2d27a1b29d..0000000000 --- a/packages/backend/src/remote/activitypub/renderer/accept.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { config } from "@/config.js"; -import type { User } from "@/models/entities/user.js"; - -export default (object: any, user: { id: User["id"]; host: null }) => ({ - type: "Accept", - actor: `${config.url}/users/${user.id}`, - object, -}); diff --git a/packages/backend/src/remote/activitypub/renderer/emoji.ts b/packages/backend/src/remote/activitypub/renderer/emoji.ts deleted file mode 100644 index dab0e8cac7..0000000000 --- a/packages/backend/src/remote/activitypub/renderer/emoji.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { config } from "@/config.js"; -import type { Emoji } from "@/models/entities/emoji.js"; - -export default (emoji: Emoji) => ({ - id: `${config.url}/emojis/${emoji.name}`, - type: "Emoji", - name: `:${emoji.name}:`, - updated: - emoji.updatedAt != null - ? emoji.updatedAt.toISOString() - : new Date().toISOString, - icon: { - type: "Image", - mediaType: emoji.type || "image/png", - url: emoji.publicUrl || emoji.originalUrl, // || emoji.originalUrl してるのは後方互換性のため - }, -}); diff --git a/packages/backend/src/remote/activitypub/renderer/flag.ts b/packages/backend/src/remote/activitypub/renderer/flag.ts deleted file mode 100644 index 1fa260be5e..0000000000 --- a/packages/backend/src/remote/activitypub/renderer/flag.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { config } from "@/config.js"; -import type { ILocalUser } from "@/models/entities/user.js"; - -// to anonymise reporters, the reporting actor must be a system user -// object has to be a uri or array of uris -export const renderFlag = ( - user: ILocalUser, - object: [string], - content: string, -) => { - return { - type: "Flag", - actor: `${config.url}/users/${user.id}`, - content, - object, - }; -}; diff --git a/packages/backend/src/remote/activitypub/renderer/follow.ts b/packages/backend/src/remote/activitypub/renderer/follow.ts deleted file mode 100644 index 30ad799507..0000000000 --- a/packages/backend/src/remote/activitypub/renderer/follow.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { config } from "@/config.js"; -import type { User } from "@/models/entities/user.js"; -import { Users } from "@/models/index.js"; - -export default ( - follower: { id: User["id"]; host: User["host"]; uri: User["host"] }, - followee: { id: User["id"]; host: User["host"]; uri: User["host"] }, - requestId?: string, -) => { - const follow = { - id: requestId ?? `${config.url}/follows/${follower.id}/${followee.id}`, - type: "Follow", - actor: Users.isLocalUser(follower) - ? `${config.url}/users/${follower.id}` - : follower.uri, - object: Users.isLocalUser(followee) - ? `${config.url}/users/${followee.id}` - : followee.uri, - } as any; - - return follow; -}; diff --git a/packages/backend/src/remote/activitypub/renderer/hashtag.ts b/packages/backend/src/remote/activitypub/renderer/hashtag.ts deleted file mode 100644 index ab6651f55d..0000000000 --- a/packages/backend/src/remote/activitypub/renderer/hashtag.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { config } from "@/config.js"; - -export default (tag: string) => ({ - type: "Hashtag", - href: `${config.url}/tags/${encodeURIComponent(tag)}`, - name: `#${tag}`, -}); diff --git a/packages/backend/src/remote/activitypub/renderer/like.ts b/packages/backend/src/remote/activitypub/renderer/like.ts index ea0df3e359..e6eab0b261 100644 --- a/packages/backend/src/remote/activitypub/renderer/like.ts +++ b/packages/backend/src/remote/activitypub/renderer/like.ts @@ -3,7 +3,7 @@ import { config } from "@/config.js"; import type { NoteReaction } from "@/models/entities/note-reaction.js"; import type { Note } from "@/models/entities/note.js"; import { Emojis } from "@/models/index.js"; -import renderEmoji from "./emoji.js"; +import { renderEmoji } from "backend-rs"; export const renderLike = async (noteReaction: NoteReaction, note: Note) => { const reaction = noteReaction.reaction; diff --git a/packages/backend/src/remote/activitypub/renderer/mention.ts b/packages/backend/src/remote/activitypub/renderer/mention.ts deleted file mode 100644 index c935c7d325..0000000000 --- a/packages/backend/src/remote/activitypub/renderer/mention.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { config } from "@/config.js"; -import type { User, ILocalUser } from "@/models/entities/user.js"; -import { Users } from "@/models/index.js"; - -export default (mention: User) => ({ - type: "Mention", - href: Users.isRemoteUser(mention) - ? mention.uri - : `${config.url}/users/${(mention as ILocalUser).id}`, - name: Users.isRemoteUser(mention) - ? `@${mention.username}@${mention.host}` - : `@${(mention as ILocalUser).username}`, -}); diff --git a/packages/backend/src/remote/activitypub/renderer/note.ts b/packages/backend/src/remote/activitypub/renderer/note.ts index dc978f1e0e..fd099cb659 100644 --- a/packages/backend/src/remote/activitypub/renderer/note.ts +++ b/packages/backend/src/remote/activitypub/renderer/note.ts @@ -6,9 +6,7 @@ import { DriveFiles, Notes, Users, Emojis, Polls } from "@/models/index.js"; import type { Emoji } from "@/models/entities/emoji.js"; import type { Poll } from "@/models/entities/poll.js"; import toHtml from "@/remote/activitypub/misc/get-note-html.js"; -import renderEmoji from "./emoji.js"; -import renderMention from "./mention.js"; -import renderHashtag from "./hashtag.js"; +import { renderEmoji, renderHashtag, renderMention } from "backend-rs"; import renderDocument from "./document.js"; export default async function renderNote( diff --git a/packages/backend/src/remote/activitypub/renderer/person.ts b/packages/backend/src/remote/activitypub/renderer/person.ts index c6371440b4..39642c1ef7 100644 --- a/packages/backend/src/remote/activitypub/renderer/person.ts +++ b/packages/backend/src/remote/activitypub/renderer/person.ts @@ -4,12 +4,11 @@ import { config } from "@/config.js"; import type { ILocalUser } from "@/models/entities/user.js"; import { DriveFiles, UserProfiles } from "@/models/index.js"; import { getUserKeypair } from "@/misc/keypair-store.js"; -import { toHtml } from "../../../mfm/to-html.js"; +import { toHtml } from "@/mfm/to-html.js"; import renderImage from "./image.js"; import renderKey from "./key.js"; import { getEmojis } from "./note.js"; -import renderEmoji from "./emoji.js"; -import renderHashtag from "./hashtag.js"; +import { renderEmoji, renderHashtag } from "backend-rs"; import type { IIdentifier } from "../models/identifier.js"; export async function renderPerson(user: ILocalUser) { diff --git a/packages/backend/src/remote/activitypub/renderer/read.ts b/packages/backend/src/remote/activitypub/renderer/read.ts deleted file mode 100644 index 9ea15b10f7..0000000000 --- a/packages/backend/src/remote/activitypub/renderer/read.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { config } from "@/config.js"; -import type { User } from "@/models/entities/user.js"; -import type { MessagingMessage } from "@/models/entities/messaging-message.js"; - -export const renderReadActivity = ( - user: { id: User["id"] }, - message: MessagingMessage, -) => ({ - type: "Read", - actor: `${config.url}/users/${user.id}`, - object: message.uri, -}); diff --git a/packages/backend/src/remote/activitypub/renderer/tombstone.ts b/packages/backend/src/remote/activitypub/renderer/tombstone.ts deleted file mode 100644 index 5c4003c75a..0000000000 --- a/packages/backend/src/remote/activitypub/renderer/tombstone.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default (id: string) => ({ - id, - type: "Tombstone", -}); diff --git a/packages/backend/src/remote/activitypub/request.ts b/packages/backend/src/remote/activitypub/request.ts index f1f496273d..22906b9bca 100644 --- a/packages/backend/src/remote/activitypub/request.ts +++ b/packages/backend/src/remote/activitypub/request.ts @@ -42,7 +42,7 @@ export default async (user: { id: User["id"] }, url: string, object: any) => { export async function apGet( url: string, user?: ILocalUser, - redirects: boolean = true, + redirects = true, ): Promise<{ finalUrl: string; content: IObject }> { if (!isSafeUrl(url)) { throw new StatusError("Invalid URL", 400); diff --git a/packages/backend/src/remote/activitypub/resolver.ts b/packages/backend/src/remote/activitypub/resolver.ts index c9d94e9fd6..48ca3e6244 100644 --- a/packages/backend/src/remote/activitypub/resolver.ts +++ b/packages/backend/src/remote/activitypub/resolver.ts @@ -6,6 +6,7 @@ import { isAllowedServer, isBlockedServer, isSelfHost, + renderFollow, } from "backend-rs"; import { apGet } from "./request.js"; import type { IObject, ICollection, IOrderedCollection } from "./type.js"; @@ -24,7 +25,6 @@ import { renderPerson } from "@/remote/activitypub/renderer/person.js"; import renderQuestion from "@/remote/activitypub/renderer/question.js"; import renderCreate from "@/remote/activitypub/renderer/create.js"; import { renderActivity } from "@/remote/activitypub/renderer/index.js"; -import renderFollow from "@/remote/activitypub/renderer/follow.js"; import { apLogger } from "@/remote/activitypub/logger.js"; import { IsNull, Not } from "typeorm"; diff --git a/packages/backend/src/server/activitypub.ts b/packages/backend/src/server/activitypub.ts index 81513f5d89..e302cb198c 100644 --- a/packages/backend/src/server/activitypub.ts +++ b/packages/backend/src/server/activitypub.ts @@ -7,9 +7,14 @@ import { renderActivity } from "@/remote/activitypub/renderer/index.js"; import renderNote from "@/remote/activitypub/renderer/note.js"; import renderKey from "@/remote/activitypub/renderer/key.js"; import { renderPerson } from "@/remote/activitypub/renderer/person.js"; -import renderEmoji from "@/remote/activitypub/renderer/emoji.js"; import { inbox as processInbox } from "@/queue/index.js"; -import { fetchMeta, getInstanceActor, isSelfHost } from "backend-rs"; +import { + fetchMeta, + getInstanceActor, + isSelfHost, + renderEmoji, + renderFollow, +} from "backend-rs"; import { Notes, Users, @@ -24,7 +29,6 @@ import { checkFetch, getSignatureUser, } from "@/remote/activitypub/check-fetch.js"; -import renderFollow from "@/remote/activitypub/renderer/follow.js"; import Featured from "./activitypub/featured.js"; import Following from "./activitypub/following.js"; import Followers from "./activitypub/followers.js"; diff --git a/packages/backend/src/server/api/common/inject-featured.ts b/packages/backend/src/server/api/common/inject-featured.ts index 30ba3eca93..3c6cb39699 100644 --- a/packages/backend/src/server/api/common/inject-featured.ts +++ b/packages/backend/src/server/api/common/inject-featured.ts @@ -19,7 +19,6 @@ export async function injectFeatured(timeline: Note[], user?: User | null) { const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで const query = Notes.createQueryBuilder("note") - .addSelect("note.score") .where("note.userHost IS NULL") .andWhere("note.score > 0") .andWhere("note.createdAt > :date", { date: new Date(Date.now() - day) }) 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 e15924d231..99c9f3d067 100644 --- a/packages/backend/src/server/api/common/read-messaging-message.ts +++ b/packages/backend/src/server/api/common/read-messaging-message.ts @@ -2,6 +2,7 @@ import { publishToChatStream, publishToGroupChatStream, publishToChatIndexStream, + renderRead, sendPushNotification, publishToMainStream, Event, @@ -13,10 +14,10 @@ import { In } from "typeorm"; import { IdentifiableError } from "@/misc/identifiable-error.js"; import type { UserGroup } from "@/models/entities/user-group.js"; import { toArray } from "@/prelude/array.js"; -import { renderReadActivity } from "@/remote/activitypub/renderer/read.js"; import { renderActivity } from "@/remote/activitypub/renderer/index.js"; import { deliver } from "@/queue/index.js"; import orderedCollection from "@/remote/activitypub/renderer/ordered-collection.js"; +import { unsafeCast } from "@/prelude/unsafe-cast.js"; /** * Mark messages as read @@ -164,7 +165,9 @@ export async function deliverReadActivity( messages: MessagingMessage | MessagingMessage[], ) { messages = toArray(messages).filter((x) => x.uri); - const contents = messages.map((x) => renderReadActivity(user, x)); + const contents = messages.map((x) => + unsafeCast>(renderRead(user.id, x.uri)), + ); if (contents.length > 1) { const collection = orderedCollection( diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index c93024b41b..bcab03469e 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -1,10 +1,9 @@ import define from "@/server/api/define.js"; import { AbuseUserReports, Users } from "@/models/index.js"; -import { getInstanceActor } from "backend-rs"; +import { getInstanceActor, renderFlag } from "backend-rs"; import { deliver } from "@/queue/index.js"; import { renderActivity } from "@/remote/activitypub/renderer/index.js"; -import { renderFlag } from "@/remote/activitypub/renderer/flag.js"; -import { ILocalUser } from "@/models/entities/user"; +import type { ILocalUser } from "@/models/entities/user"; export const meta = { tags: ["admin"], @@ -35,7 +34,7 @@ export default define(meta, paramDef, async (ps, me) => { deliver( actor.id, - renderActivity(renderFlag(actor, [targetUser.uri!], report.comment)), + renderActivity(await renderFlag(targetUser.uri, report.comment)), targetUser.inbox, ); } diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 4f90145ec3..0d0f2cd704 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -42,7 +42,6 @@ export default define(meta, paramDef, async (ps, user) => { const day = 1000 * 60 * 60 * 24 * ps.days; const query = Notes.createQueryBuilder("note") - .addSelect("note.score") .andWhere("note.score > 0") .andWhere("note.createdAt > :date", { date: new Date(Date.now() - day) }) .andWhere("note.visibility = 'public'") diff --git a/packages/backend/src/server/api/mastodon/helpers/misc.ts b/packages/backend/src/server/api/mastodon/helpers/misc.ts index 01a321273b..9d6b374eb1 100644 --- a/packages/backend/src/server/api/mastodon/helpers/misc.ts +++ b/packages/backend/src/server/api/mastodon/helpers/misc.ts @@ -360,7 +360,6 @@ export class MiscHelpers { ): Promise { if (limit > 40) limit = 40; const query = Notes.createQueryBuilder("note") - .addSelect("note.score") .andWhere("note.score > 0") .andWhere("note.createdAt > :date", { date: new Date(Date.now() - 1000 * 60 * 60 * 24), diff --git a/packages/backend/src/server/file/byte-range-readable.ts b/packages/backend/src/server/file/byte-range-readable.ts index 9699f95092..d557245c48 100644 --- a/packages/backend/src/server/file/byte-range-readable.ts +++ b/packages/backend/src/server/file/byte-range-readable.ts @@ -82,7 +82,7 @@ function extractRanges( } function createBoundary(len: number): string { - let chars = []; + const chars = []; for (let i = 0; i < len; i = i + 1) { chars[i] = BOUNDARY_CHARS.charAt( Math.floor(Math.random() * BOUNDARY_CHARS.length), diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts index 6db3c37a15..685d86f7c6 100644 --- a/packages/backend/src/server/index.ts +++ b/packages/backend/src/server/index.ts @@ -15,8 +15,7 @@ import { IsNull } from "typeorm"; import { config } from "@/config.js"; import Logger from "@/services/logger.js"; import { Users } from "@/models/index.js"; -import { fetchMeta, stringToAcct } from "backend-rs"; -import { genIdenticon } from "@/misc/gen-identicon.js"; +import { fetchMeta, genIdenticon, stringToAcct } from "backend-rs"; import { createTemp } from "@/misc/create-temp.js"; import activityPub from "./activitypub.js"; import nodeinfo from "./nodeinfo.js"; @@ -120,7 +119,7 @@ router.get("/identicon/:x", async (ctx) => { const instanceMeta = await fetchMeta(); if (instanceMeta.enableIdenticonGeneration) { const [temp, cleanup] = await createTemp(); - await genIdenticon(ctx.params.x, fs.createWriteStream(temp)); + fs.writeFileSync(temp, await genIdenticon(ctx.params.x)); ctx.set("Content-Type", "image/png"); ctx.body = fs.createReadStream(temp).on("close", () => cleanup()); } else { diff --git a/packages/backend/src/server/web/bios.css b/packages/backend/src/server/web/bios.css index d6a1285e94..db00beae8d 100644 --- a/packages/backend/src/server/web/bios.css +++ b/packages/backend/src/server/web/bios.css @@ -1,6 +1,6 @@ main > .tabs { padding: 16px; - border-bottom: 4px solid #908caa; + border-block-end: 4px solid #908caa; } #lsEditor > .adder { margin: 16px; @@ -9,21 +9,21 @@ main > .tabs { } #lsEditor > .adder > textarea { display: block; - width: 100%; - min-height: 5em; + inline-size: 100%; + min-block-size: 5em; box-sizing: border-box; } #lsEditor > .record { padding: 16px; - border-bottom: 1px solid #908caa; + border-block-end: 1px solid #908caa; } #lsEditor > .record > header { font-weight: 700; } #lsEditor > .record > textarea { display: block; - width: 100%; - min-height: 5em; + inline-size: 100%; + min-block-size: 5em; box-sizing: border-box; } @@ -36,7 +36,7 @@ main { } #tl > div { padding: 16px; - border-bottom: 1px solid #908caa; + border-block-end: 1px solid #908caa; } #tl > div > header { font-weight: 700; @@ -59,11 +59,14 @@ html { } button { border-radius: 999px; - padding: 0px 12px 0px 12px; + padding-block-start: 0px; + padding-inline-end: 12px; + padding-block-end: 0px; + padding-inline-start: 12px; border: none; cursor: pointer; - margin-bottom: 12px; - background: linear-gradient(90deg, rgb(156, 207, 216), rgb(49, 116, 143)); + margin-block-end: 12px; + background: linear-gradient(var(--gradient-to-inline-end), rgb(156, 207, 216), rgb(49, 116, 143)); line-height: 50px; color: #191724; font-weight: bold; @@ -72,25 +75,29 @@ button { } button { border-radius: 999px; - padding: 0px 12px 0px 12px; + padding-block-start: 0px; + padding-inline-end: 12px; + padding-block-end: 0px; + padding-inline-start: 12px; border: none; cursor: pointer; - margin-bottom: 12px; + margin-block-end: 12px; } button { background: #444; line-height: 40px; color: rgb(156, 207, 216); font-size: 16px; - padding: 0 20px; - margin-right: 5px; - margin-left: 5px; + padding-block: 0; + padding-inline: 20px; + margin-inline-end: 5px; + margin-inline-start: 5px; } button:hover { background: #555; } #ls { - background: linear-gradient(90deg, rgb(156, 207, 216), rgb(49, 116, 143)); + background: linear-gradient(var(--gradient-to-inline-end), rgb(156, 207, 216), rgb(49, 116, 143)); line-height: 30px; color: #191724; font-weight: bold; @@ -120,10 +127,10 @@ textarea { border: solid #aaa; border-radius: 10px; color: #e0def4; - margin-top: 1rem; - margin-bottom: 1rem; - width: 20rem; - height: 7.5rem; + margin-block-start: 1rem; + margin-block-end: 1rem; + inline-size: 20rem; + block-size: 7.5rem; padding: 0.5rem; } @@ -135,10 +142,10 @@ input { border: solid #aaa; border-radius: 10px; color: #e0def4; - margin-top: 1rem; - margin-bottom: 1rem; - width: 10rem; - height: 1rem; + margin-block-start: 1rem; + margin-block-end: 1rem; + inline-size: 10rem; + block-size: 1rem; padding: 0.5rem; } diff --git a/packages/backend/src/server/web/cli.css b/packages/backend/src/server/web/cli.css index 740a2aa1a2..311a962e59 100644 --- a/packages/backend/src/server/web/cli.css +++ b/packages/backend/src/server/web/cli.css @@ -10,7 +10,7 @@ main { border-radius: 10px; margin: 10px; padding: 10px; - width: fit-content; + inline-size: fit-content; } #tl > div > header { font-weight: 700; @@ -19,7 +19,7 @@ main { img { border-radius: 10px; - margin-right: 10px; + margin-inline-end: 10px; } #form { @@ -42,11 +42,11 @@ html { button { border-radius:999px; padding:0 40px; - margin-top: 1rem; + margin-block-start: 1rem; border:none; cursor:pointer; - margin-bottom:12px; - background:linear-gradient(90deg,#9ccfd8,#31748f); + margin-block-end:12px; + background:linear-gradient(var(--gradient-to-inline-end),#9ccfd8,#31748f); line-height:50px; color:#191724; font-weight:700; @@ -75,9 +75,9 @@ code { border: solid #aaa; border-radius: 10px; color: #e0def4; - margin-top: 3rem; - width: 20rem; - height: 5rem; + margin-block-start: 3rem; + inline-size: 20rem; + block-size: 5rem; padding: 0.5rem; } @@ -85,8 +85,8 @@ code { border: solid #eee; } -@media screen and (max-width: 500px) { +@media screen and (max-inline-size: 500px) { #text { - width: 80% + inline-size: 80% } } diff --git a/packages/backend/src/server/web/style.css b/packages/backend/src/server/web/style.css index 2a93253992..6477874698 100644 --- a/packages/backend/src/server/web/style.css +++ b/packages/backend/src/server/web/style.css @@ -23,10 +23,10 @@ html { #splash { position: fixed; z-index: 10000; - top: 0; - left: 0; - width: 100vw; - height: 100vh; + inset-block-start: 0; + inset-inline-start: 0; + inline-size: 100vi; + block-size: 100vb; cursor: wait; background-color: var(--bg); opacity: 1; @@ -35,36 +35,30 @@ html { #splashIcon { position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; + inset: 0; margin: auto; - width: 64px; - height: 64px; + inline-size: 64px; + block-size: 64px; pointer-events: none; } #splashSpinner { position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; + inset: 0; margin: auto; display: inline-block; - width: 28px; - height: 28px; + inline-size: 28px; + block-size: 28px; transform: translateY(110px); display: none; color: var(--accent); } #splashSpinner > .spinner { position: absolute; - top: 0; - left: 0; - width: 28px; - height: 28px; + inset-block-start: 0; + inset-inline-start: 0; + inline-size: 28px; + block-size: 28px; fill-rule: evenodd; clip-rule: evenodd; stroke-linecap: round; @@ -123,15 +117,12 @@ html { #splashText { position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; + inset: 0; margin: auto; display: inline-block; - width: 70%; - height: 0; + inline-size: 70%; + block-size: 0; text-align: center; - padding-top: 100px; + padding-block-start: 100px; font-family: "Atkinson Hyperlegible", sans-serif; } diff --git a/packages/backend/src/services/blocking/create.ts b/packages/backend/src/services/blocking/create.ts index 1d7b80ffbf..9bc59e70e9 100644 --- a/packages/backend/src/services/blocking/create.ts +++ b/packages/backend/src/services/blocking/create.ts @@ -1,5 +1,4 @@ import { renderActivity } from "@/remote/activitypub/renderer/index.js"; -import renderFollow from "@/remote/activitypub/renderer/follow.js"; import { renderUndo } from "@/remote/activitypub/renderer/undo.js"; import { renderBlock } from "@/remote/activitypub/renderer/block.js"; import { deliver } from "@/queue/index.js"; @@ -20,6 +19,7 @@ import { publishToMainStream, publishToUserStream, UserEvent, + renderFollow, } from "backend-rs"; import { getActiveWebhooks } from "@/misc/webhook-cache.js"; import { webhookDeliver } from "@/queue/index.js"; diff --git a/packages/backend/src/services/fetch-instance-metadata.ts b/packages/backend/src/services/fetch-instance-metadata.ts index 494027a37c..d0bcdd33a4 100644 --- a/packages/backend/src/services/fetch-instance-metadata.ts +++ b/packages/backend/src/services/fetch-instance-metadata.ts @@ -156,7 +156,7 @@ async function fetchFaviconUrl( // TODO //timeout: 10000, agent: getAgentByUrl, - size: 1024 * 1024 + size: 1024 * 1024, }); if (favicon.ok) { diff --git a/packages/backend/src/services/following/create.ts b/packages/backend/src/services/following/create.ts index aea5bd48a5..c5821786c8 100644 --- a/packages/backend/src/services/following/create.ts +++ b/packages/backend/src/services/following/create.ts @@ -1,6 +1,4 @@ import { renderActivity } from "@/remote/activitypub/renderer/index.js"; -import renderFollow from "@/remote/activitypub/renderer/follow.js"; -import renderAccept from "@/remote/activitypub/renderer/accept.js"; import renderReject from "@/remote/activitypub/renderer/reject.js"; import { deliver } from "@/queue/index.js"; import createFollowRequest from "./requests/create.js"; @@ -23,6 +21,8 @@ import { publishToMainStream, publishToUserStream, UserEvent, + renderAccept, + renderFollow, } from "backend-rs"; import { createNotification } from "@/services/create-notification.js"; import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js"; @@ -275,7 +275,7 @@ export default async function ( if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) { const content = renderActivity( - renderAccept(renderFollow(follower, followee, requestId), followee), + renderAccept(followee.id, renderFollow(follower, followee, requestId)), ); deliver(followee.id, content, follower.inbox); } diff --git a/packages/backend/src/services/following/delete.ts b/packages/backend/src/services/following/delete.ts index 9d0135b81f..e46d8422db 100644 --- a/packages/backend/src/services/following/delete.ts +++ b/packages/backend/src/services/following/delete.ts @@ -3,9 +3,9 @@ import { publishToMainStream, publishToUserStream, UserEvent, + renderFollow, } from "backend-rs"; import { renderActivity } from "@/remote/activitypub/renderer/index.js"; -import renderFollow from "@/remote/activitypub/renderer/follow.js"; import { renderUndo } from "@/remote/activitypub/renderer/undo.js"; import renderReject from "@/remote/activitypub/renderer/reject.js"; import { deliver, webhookDeliver } from "@/queue/index.js"; diff --git a/packages/backend/src/services/following/reject.ts b/packages/backend/src/services/following/reject.ts index 1523573bf9..aaed1df3a8 100644 --- a/packages/backend/src/services/following/reject.ts +++ b/packages/backend/src/services/following/reject.ts @@ -1,5 +1,4 @@ import { renderActivity } from "@/remote/activitypub/renderer/index.js"; -import renderFollow from "@/remote/activitypub/renderer/follow.js"; import renderReject from "@/remote/activitypub/renderer/reject.js"; import { deliver, webhookDeliver } from "@/queue/index.js"; import { @@ -7,6 +6,7 @@ import { publishToMainStream, publishToUserStream, UserEvent, + renderFollow, } from "backend-rs"; import type { ILocalUser, IRemoteUser } from "@/models/entities/user.js"; import { Users, FollowRequests, Followings } from "@/models/index.js"; diff --git a/packages/backend/src/services/following/requests/accept.ts b/packages/backend/src/services/following/requests/accept.ts index bcd7d2cf4c..ba1d40159d 100644 --- a/packages/backend/src/services/following/requests/accept.ts +++ b/packages/backend/src/services/following/requests/accept.ts @@ -1,8 +1,11 @@ import { renderActivity } from "@/remote/activitypub/renderer/index.js"; -import renderFollow from "@/remote/activitypub/renderer/follow.js"; -import renderAccept from "@/remote/activitypub/renderer/accept.js"; import { deliver } from "@/queue/index.js"; -import { Event, publishToMainStream } from "backend-rs"; +import { + Event, + publishToMainStream, + renderAccept, + renderFollow, +} from "backend-rs"; import { insertFollowingDoc } from "../create.js"; import type { User, CacheableUser } from "@/models/entities/user.js"; import { FollowRequests, Users } from "@/models/index.js"; @@ -35,8 +38,8 @@ export default async function ( if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) { const content = renderActivity( renderAccept( + followee.id, renderFollow(follower, followee, request.requestId!), - followee, ), ); deliver(followee.id, content, follower.inbox); diff --git a/packages/backend/src/services/following/requests/cancel.ts b/packages/backend/src/services/following/requests/cancel.ts index eb1b2dcb49..435c86f19b 100644 --- a/packages/backend/src/services/following/requests/cancel.ts +++ b/packages/backend/src/services/following/requests/cancel.ts @@ -1,8 +1,7 @@ import { renderActivity } from "@/remote/activitypub/renderer/index.js"; -import renderFollow from "@/remote/activitypub/renderer/follow.js"; import { renderUndo } from "@/remote/activitypub/renderer/undo.js"; import { deliver } from "@/queue/index.js"; -import { Event, publishToMainStream } from "backend-rs"; +import { Event, publishToMainStream, renderFollow } from "backend-rs"; import { IdentifiableError } from "@/misc/identifiable-error.js"; import type { User } from "@/models/entities/user.js"; import { Users, FollowRequests } from "@/models/index.js"; diff --git a/packages/backend/src/services/following/requests/create.ts b/packages/backend/src/services/following/requests/create.ts index 40f383cfd3..cc6b4261eb 100644 --- a/packages/backend/src/services/following/requests/create.ts +++ b/packages/backend/src/services/following/requests/create.ts @@ -1,9 +1,8 @@ import { renderActivity } from "@/remote/activitypub/renderer/index.js"; -import renderFollow from "@/remote/activitypub/renderer/follow.js"; import { deliver } from "@/queue/index.js"; import type { User } from "@/models/entities/user.js"; import { Blockings, FollowRequests, Users } from "@/models/index.js"; -import { Event, genIdAt, publishToMainStream } from "backend-rs"; +import { Event, genIdAt, publishToMainStream, renderFollow } from "backend-rs"; import { createNotification } from "@/services/create-notification.js"; import { config } from "@/config.js"; diff --git a/packages/backend/src/services/logger.ts b/packages/backend/src/services/logger.ts index f7843eac18..24ae1458fe 100644 --- a/packages/backend/src/services/logger.ts +++ b/packages/backend/src/services/logger.ts @@ -132,7 +132,7 @@ export default class Logger { ? message : null; - let log = `${l} ${worker}\t[${domains.join(" ")}]\t${m}`; + const log = `${l} ${worker}\t[${domains.join(" ")}]\t${m}`; console.log(important ? chalk.bold(log) : log); diff --git a/packages/backend/src/services/messages/delete.ts b/packages/backend/src/services/messages/delete.ts index f0b65151ec..aa9eb0ed54 100644 --- a/packages/backend/src/services/messages/delete.ts +++ b/packages/backend/src/services/messages/delete.ts @@ -1,10 +1,12 @@ -import { config } from "@/config.js"; import { MessagingMessages, Users } from "@/models/index.js"; import type { MessagingMessage } from "@/models/entities/messaging-message.js"; -import { publishToChatStream, publishToGroupChatStream } from "backend-rs"; +import { + publishToChatStream, + publishToGroupChatStream, + renderTombstone, +} from "backend-rs"; import { renderActivity } from "@/remote/activitypub/renderer/index.js"; import renderDelete from "@/remote/activitypub/renderer/delete.js"; -import renderTombstone from "@/remote/activitypub/renderer/tombstone.js"; import { deliver } from "@/queue/index.js"; export async function deleteMessage(message: MessagingMessage) { @@ -36,10 +38,7 @@ async function postDeleteMessage(message: MessagingMessage) { if (Users.isLocalUser(user) && Users.isRemoteUser(recipient)) { const activity = renderActivity( - renderDelete( - renderTombstone(`${config.url}/notes/${message.id}`), - user, - ), + renderDelete(renderTombstone(message.id), user), ); deliver(user.id, activity, recipient.inbox); } diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index 4b21c67141..63c6239ccf 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -62,7 +62,7 @@ import { db } from "@/db/postgre.js"; import { getActiveWebhooks } from "@/misc/webhook-cache.js"; import { redisClient } from "@/db/redis.js"; import { Mutex } from "redis-semaphore"; -import { langmap } from "firefish-js"; +import { bcp47Pattern } from "firefish-js"; import Logger from "@/services/logger.js"; import { inspect } from "node:util"; import { toRustObject } from "@/prelude/undefined-to-null.js"; @@ -273,8 +273,7 @@ export default async ( data.text = data.text?.trim() ?? null; if (data.lang != null) { - if (!Object.keys(langmap).includes(data.lang.toLowerCase())) - throw new Error("invalid param"); + if (!bcp47Pattern.test(data.lang)) rej("Invalid language code"); data.lang = data.lang.toLowerCase(); } else { data.lang = null; @@ -317,7 +316,7 @@ export default async ( } if (!isDraft && data.visibility === "specified") { - if (data.visibleUsers == null) throw new Error("invalid param"); + if (data.visibleUsers == null) rej("invalid param"); for (const u of data.visibleUsers) { if (!mentionedUsers.some((x) => x.id === u.id)) { @@ -444,7 +443,7 @@ export default async ( // 未読通知を作成 if (data.visibility === "specified") { - if (data.visibleUsers == null) throw new Error("invalid param"); + if (data.visibleUsers == null) rej("invalid param"); for (const u of data.visibleUsers) { // ローカルユーザーのみ diff --git a/packages/backend/src/services/note/delete.ts b/packages/backend/src/services/note/delete.ts index 0ae03a60ac..f599a55470 100644 --- a/packages/backend/src/services/note/delete.ts +++ b/packages/backend/src/services/note/delete.ts @@ -3,7 +3,6 @@ import renderDelete from "@/remote/activitypub/renderer/delete.js"; import renderAnnounce from "@/remote/activitypub/renderer/announce.js"; import { renderUndo } from "@/remote/activitypub/renderer/undo.js"; import { renderActivity } from "@/remote/activitypub/renderer/index.js"; -import renderTombstone from "@/remote/activitypub/renderer/tombstone.js"; import { config } from "@/config.js"; import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js"; import type { Note, IMentionedRemoteUsers } from "@/models/entities/note.js"; @@ -16,7 +15,7 @@ import { countSameRenotes } from "@/misc/count-same-renotes.js"; import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js"; import { deliverToRelays } from "@/services/relay.js"; import type { IActivity } from "@/remote/activitypub/type.js"; -import { NoteEvent, publishToNoteStream } from "backend-rs"; +import { NoteEvent, publishToNoteStream, renderTombstone } from "backend-rs"; async function recalculateNotesCountOfLocalUser(user: { id: User["id"]; @@ -102,7 +101,7 @@ export default async function ( ), user.id, ) - : renderDelete(renderTombstone(`${config.url}/notes/${note.id}`), user), + : renderDelete(renderTombstone(note.id), user), ); deliverToConcerned(user, note, content); @@ -127,10 +126,7 @@ export default async function ( affectedLocalUsers[cascadingNote.user.id] ??= cascadingNote.user; if (cascadingNote.localOnly) continue; // filter out local-only notes const content = renderActivity( - renderDelete( - renderTombstone(`${config.url}/notes/${cascadingNote.id}`), - cascadingNote.user, - ), + renderDelete(renderTombstone(cascadingNote.id), cascadingNote.user), ); deliverToConcerned(cascadingNote.user, cascadingNote, content); } diff --git a/packages/client/package.json b/packages/client/package.json index eb93c9d0ec..20e35d72f4 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -8,10 +8,9 @@ "build": "pnpm vite build", "build:debug": "pnpm run build", "lint": "pnpm run lint:ts ; pnpm run lint:vue", - "lint:ts": "pnpm biome check **/*.ts --write", - "lint:vue": "pnpm biome check **/*.vue --write", - "types:check": "pnpm vue-tsc --noEmit", - "format": "pnpm biome format * --write" + "lint:ts": "pnpm biome check --write src/*.ts src/**/*.ts src/**/**/*.ts", + "lint:vue": "pnpm vue-tsc --noEmit", + "format": "pnpm biome format --write src" }, "devDependencies": { "@phosphor-icons/web": "2.1.1", @@ -33,8 +32,8 @@ "@types/throttle-debounce": "5.0.2", "@types/tinycolor2": "1.4.6", "@types/uuid": "10.0.0", - "@vitejs/plugin-vue": "5.1.1", - "@vue/runtime-core": "3.4.34", + "@vitejs/plugin-vue": "5.1.2", + "@vue/runtime-core": "3.4.37", "autobind-decorator": "2.4.0", "autosize": "6.0.1", "broadcast-channel": "7.0.0", @@ -50,7 +49,7 @@ "date-fns": "3.6.0", "emojilib": "3.0.12", "eventemitter3": "5.0.1", - "fast-blurhash": "1.1.2", + "fast-blurhash": "1.1.4", "firefish-js": "workspace:*", "focus-trap": "7.5.4", "focus-trap-vue": "4.0.3", @@ -68,14 +67,14 @@ "photoswipe": "5.4.4", "prismjs": "1.29.0", "punycode": "2.3.1", - "qrcode": "1.5.3", + "qrcode": "1.5.4", "qrcode-vue3": "1.6.8", - "rollup": "4.19.1", + "rollup": "4.20.0", "s-age": "1.1.2", "sass": "1.77.8", "seedrandom": "3.0.5", "stringz": "2.1.0", - "swiper": "11.1.8", + "swiper": "11.1.9", "textarea-caret": "3.1.0", "throttle-debounce": "5.0.2", "tinycolor2": "1.6.0", @@ -83,10 +82,10 @@ "typescript": "5.5.4", "unicode-emoji-json": "0.6.0", "uuid": "10.0.0", - "vite": "5.3.5", + "vite": "5.4.0", "vite-plugin-compression": "0.5.1", - "vue": "3.4.34", - "vue-draggable-plus": "0.5.2", + "vue": "3.4.37", + "vue-draggable-plus": "0.5.3", "vue-plyr": "7.0.0", "vue-prism-editor": "2.0.0-alpha.2", "vue-tsc": "2.0.29" diff --git a/packages/client/src/components/MkAbuseReport.vue b/packages/client/src/components/MkAbuseReport.vue index b190652052..a361f2fc54 100644 --- a/packages/client/src/components/MkAbuseReport.vue +++ b/packages/client/src/components/MkAbuseReport.vue @@ -99,11 +99,11 @@ function resolve() { display: flex; > .target { - width: 35%; + inline-size: 35%; box-sizing: border-box; - text-align: left; + text-align: start; padding: 24px; - border-right: solid 1px var(--divider); + border-inline-end: solid 1px var(--divider); > .info { display: flex; @@ -125,13 +125,14 @@ function resolve() { background-size: 16px 16px; > .avatar { - width: 42px; - height: 42px; + inline-size: 42px; + block-size: 42px; } > .names { - margin-left: 0.3em; - padding: 0 8px; + margin-inline-start: 0.3em; + padding-block: 0; + padding-inline: 8px; flex: 1; > .name { diff --git a/packages/client/src/components/MkAbuseReportWindow.vue b/packages/client/src/components/MkAbuseReportWindow.vue index bde54b7d7e..3f3b02aa5a 100644 --- a/packages/client/src/components/MkAbuseReportWindow.vue +++ b/packages/client/src/components/MkAbuseReportWindow.vue @@ -9,7 +9,7 @@ - + @@ -156,7 +156,7 @@ function getStatus(instance) { .taeiyria { > .query { background: var(--bg); - margin-bottom: 16px; + margin-block-end: 16px; } } diff --git a/packages/client/src/pages/about.vue b/packages/client/src/pages/about.vue index 3102f31c9c..8ce82224fc 100644 --- a/packages/client/src/pages/about.vue +++ b/packages/client/src/pages/about.vue @@ -301,6 +301,11 @@ let swiperRef: SwiperType | null = null; function setSwiperRef(swiper: SwiperType) { swiperRef = swiper; syncSlide(tabs.indexOf(tab.value)); + const styles = getComputedStyle(swiper.el); + swiper.changeLanguageDirection(styles.direction as "rtl" | "ltr"); + if (styles["writing-mode"].startsWith("vertical")) { + swiper.changeDirection("vertical"); + } } function onSlideChange() { @@ -325,8 +330,11 @@ function syncSlide(index: number) { > .icon { display: block; - margin: 16px auto 0 auto; - height: 64px; + margin-block-start: 16px; + margin-inline-end: auto; + margin-block-end: 0; + margin-inline-start: auto; + block-size: 64px; } > .spin { diff --git a/packages/client/src/pages/admin-file.vue b/packages/client/src/pages/admin-file.vue index b2778ddbbf..c6dd208bd7 100644 --- a/packages/client/src/pages/admin-file.vue +++ b/packages/client/src/pages/admin-file.vue @@ -264,6 +264,11 @@ let swiperRef = null; function setSwiperRef(swiper) { swiperRef = swiper; syncSlide(tabs.indexOf(tab.value)); + const styles = getComputedStyle(swiper.el); + swiper.changeLanguageDirection(styles.direction as "rtl" | "ltr"); + if (styles["writing-mode"].startsWith("vertical")) { + swiper.changeDirection("vertical"); + } } function onSlideChange() { @@ -281,8 +286,8 @@ function syncSlide(index) { display: block; > .thumbnail { - height: 300px; - max-width: 100%; + block-size: 300px; + max-inline-size: 100%; } } diff --git a/packages/client/src/pages/admin/_header_.vue b/packages/client/src/pages/admin/_header_.vue index 5992438654..272adf651a 100644 --- a/packages/client/src/pages/admin/_header_.vue +++ b/packages/client/src/pages/admin/_header_.vue @@ -40,7 +40,7 @@ class="fullButton" primary @click.stop="action.handler" - >{{ action.text }}