Merge branch 'develop' into 'main'
release: v20240725 Co-authored-by: GitLab CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev> Co-authored-by: Hosted Weblate <hosted@weblate.org> Co-authored-by: Linerly <linerly@proton.me> Co-authored-by: jolupa <jolupameister@gmail.com> See merge request firefish/firefish!11196
This commit is contained in:
commit
eeed97cd96
149 changed files with 1847 additions and 7838 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -34,6 +34,7 @@ coverage
|
|||
!/.config/helm_values_example.yml
|
||||
!/.config/LICENSE
|
||||
/docker-compose.yml
|
||||
/compose.yml
|
||||
/custom/*
|
||||
!/custom/LICENSE
|
||||
|
||||
|
|
336
Cargo.lock
generated
336
Cargo.lock
generated
|
@ -92,7 +92,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -143,7 +143,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -154,7 +154,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -235,6 +235,7 @@ dependencies = [
|
|||
"url",
|
||||
"urlencoding",
|
||||
"web-push",
|
||||
"zhconv",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -398,9 +399,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
|||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
|
@ -410,13 +411,12 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.0"
|
||||
version = "1.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8"
|
||||
checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -500,6 +500,16 @@ dependencies = [
|
|||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.6.2"
|
||||
|
@ -629,7 +639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -681,6 +691,12 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "daachorse"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b7ef7a4be509357f4804d0a22e830daddb48f19fd604e4ad32ddce04a94c36"
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.4.5"
|
||||
|
@ -772,7 +788,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1188,6 +1204,12 @@ version = "0.4.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hex-literal"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.12.4"
|
||||
|
@ -1388,7 +1410,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1415,12 +1437,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.1"
|
||||
version = "0.25.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11"
|
||||
checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"byteorder-lite",
|
||||
"color_quant",
|
||||
"gif",
|
||||
"image-webp",
|
||||
|
@ -1435,12 +1457,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "image-webp"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d730b085583c4d789dfd07fdcf185be59501666a90c97c40162b37e4fdad272d"
|
||||
checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904"
|
||||
dependencies = [
|
||||
"byteorder-lite",
|
||||
"thiserror",
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1467,7 +1489,7 @@ checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1496,7 +1518,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1518,6 +1540,8 @@ dependencies = [
|
|||
"mime",
|
||||
"once_cell",
|
||||
"polling",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"slab",
|
||||
"sluice",
|
||||
"tracing",
|
||||
|
@ -1526,6 +1550,15 @@ dependencies = [
|
|||
"waker-fn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
|
@ -1642,9 +1675,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
|
||||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.6",
|
||||
|
@ -1744,7 +1777,7 @@ dependencies = [
|
|||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1797,20 +1830,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi"
|
||||
version = "3.0.0-alpha.6"
|
||||
version = "3.0.0-alpha.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e5a69ce63aa1e68c939c5afa3f7be80d0c37eb3755022b064792dc019f08d8e"
|
||||
checksum = "743b5a7769f54c95e20a26d9e66d1b43d5622b7dc8ec8f97b51ed8c58633841f"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"chrono",
|
||||
|
@ -1831,23 +1865,23 @@ checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a"
|
|||
|
||||
[[package]]
|
||||
name = "napi-derive"
|
||||
version = "3.0.0-alpha.5"
|
||||
version = "3.0.0-alpha.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e82f3209766c72466e28f05d8e55931cfda1652877b2cadf4011034890a2770"
|
||||
checksum = "c7619cfcc3985e1ed73d147d6950caabaedabcf5c98133502f9d18c3d0061320"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"convert_case",
|
||||
"napi-derive-backend",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi-derive-backend"
|
||||
version = "2.0.0-alpha.5"
|
||||
version = "2.0.0-alpha.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b17d6c84ea7366a126d850e2010f2d8354be1d3f2c62bc20751b08ba5b0a774"
|
||||
checksum = "584f6a91c05e8c6bf80622fcc2675c7d27934754d4f1141cfd422d531a3f51fb"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"once_cell",
|
||||
|
@ -1855,7 +1889,7 @@ dependencies = [
|
|||
"quote",
|
||||
"regex",
|
||||
"semver",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1984,7 +2018,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2029,12 +2063,11 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
name = "num_threads"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
@ -2055,9 +2088,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
|||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.64"
|
||||
version = "0.10.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
|
||||
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
|
@ -2076,7 +2109,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2096,9 +2129,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.102"
|
||||
version = "0.9.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
|
||||
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
@ -2137,7 +2170,7 @@ dependencies = [
|
|||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2194,7 +2227,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.5.2",
|
||||
"redox_syscall 0.5.3",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
@ -2295,7 +2328,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2468,7 +2501,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2530,7 +2563,7 @@ dependencies = [
|
|||
"built",
|
||||
"cfg-if",
|
||||
"interpolate_name",
|
||||
"itertools",
|
||||
"itertools 0.12.1",
|
||||
"libc",
|
||||
"libfuzzer-sys",
|
||||
"log",
|
||||
|
@ -2616,9 +2649,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
|
||||
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
@ -2664,9 +2697,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.44"
|
||||
version = "0.8.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aee83dc281d5a3200d37b299acd13b81066ea126a7f16f0eae70fc9aed241d9"
|
||||
checksum = "ade4539f42266ded9e755c605bdddf546242b2c961b03b06a7375260788a0523"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
@ -2798,6 +2831,23 @@ dependencies = [
|
|||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||
|
||||
[[package]]
|
||||
name = "ruzstd"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3ffab8f9715a0d455df4bbb9d21e91135aab3cd3ca187af0cd0c3c3f868fdc"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"thiserror-core",
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
|
@ -2839,7 +2889,7 @@ dependencies = [
|
|||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2860,7 +2910,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"strum",
|
||||
"strum 0.25.0",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tracing",
|
||||
|
@ -2878,7 +2928,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"sea-bae",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
|
@ -2955,7 +3005,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3381,12 +3431,34 @@ dependencies = [
|
|||
"unicode-properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
|
@ -3406,9 +3478,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.71"
|
||||
version = "2.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
|
||||
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -3435,7 +3507,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3485,22 +3557,42 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.62"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.62"
|
||||
name = "thiserror-core"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
|
||||
checksum = "c001ee18b7e5e3f62cbf58c7fe220119e68d902bb7443179c0c8aef30090e999"
|
||||
dependencies = [
|
||||
"thiserror-core-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-core-impl"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4c60d69f36615a077cc7663b9cb8e42275722d23e58a7fa3d2c7f2915d09d04"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3531,7 +3623,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num-conv",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
|
@ -3581,31 +3676,30 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.38.0"
|
||||
version = "1.39.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
||||
checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3647,9 +3741,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.14"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
|
||||
checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
|
@ -3668,9 +3762,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.15"
|
||||
version = "0.22.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1"
|
||||
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
|
@ -3699,7 +3793,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3733,6 +3827,16 @@ dependencies = [
|
|||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "1.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
|
@ -3851,6 +3955,18 @@ version = "0.2.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "8.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
"rustversion",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.2.0"
|
||||
|
@ -3911,7 +4027,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
@ -3933,7 +4049,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
@ -4169,9 +4285,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.13"
|
||||
version = "0.6.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
|
||||
checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -4214,7 +4330,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
"synstructure 0.13.1",
|
||||
]
|
||||
|
||||
|
@ -4235,7 +4351,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4255,7 +4371,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
"synstructure 0.13.1",
|
||||
]
|
||||
|
||||
|
@ -4284,7 +4400,57 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zhconv"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a5764e8c3c48dce7dd281cdae65c785536d1da3078b484c2254e7bea7b42323"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"daachorse",
|
||||
"hex-literal",
|
||||
"itertools 0.10.5",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"ruzstd",
|
||||
"sha2",
|
||||
"strum 0.24.1",
|
||||
"vergen",
|
||||
"wasm-bindgen",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "6.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.12+zstd.1.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -10,8 +10,8 @@ rust-version = "1.74"
|
|||
macros = { path = "packages/macro-rs/macros" }
|
||||
macros-impl = { path = "packages/macro-rs/macros-impl" }
|
||||
|
||||
napi = "3.0.0-alpha.6"
|
||||
napi-derive = "3.0.0-alpha.5"
|
||||
napi = "3.0.0-alpha.8"
|
||||
napi-derive = "3.0.0-alpha.7"
|
||||
napi-build = "2.1.3"
|
||||
|
||||
argon2 = { version = "0.5.3", default-features = false }
|
||||
|
@ -24,7 +24,7 @@ convert_case = { version = "0.6.0", default-features = false }
|
|||
cuid2 = { version = "0.1.2", default-features = false }
|
||||
emojis = { version = "0.6.2", default-features = false }
|
||||
idna = { version = "1.0.2", default-features = false }
|
||||
image = { version = "0.25.1", default-features = false }
|
||||
image = { version = "0.25.2", default-features = false }
|
||||
isahc = { version = "1.7.2", default-features = false }
|
||||
nom-exif = { version = "1.2.6", default-features = false }
|
||||
once_cell = { version = "1.19.0", default-features = false }
|
||||
|
@ -39,20 +39,21 @@ 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 }
|
||||
serde_yaml = { version = "0.9.34", default-features = false }
|
||||
syn = { version = "2.0.71", default-features = false }
|
||||
syn = { version = "2.0.72", default-features = false }
|
||||
sysinfo = { version = "0.30.13", default-features = false }
|
||||
thiserror = { version = "1.0.62", default-features = false }
|
||||
tokio = { version = "1.38.0", default-features = false }
|
||||
thiserror = { version = "1.0.63", default-features = false }
|
||||
tokio = { version = "1.39.1", default-features = false }
|
||||
tokio-test = { version = "0.4.4", default-features = false }
|
||||
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 }
|
||||
zhconv = "0.3.1"
|
||||
|
||||
# subdependencies
|
||||
## explicitly list OpenSSL to use the vendored version
|
||||
openssl = "0.10.64"
|
||||
openssl = "0.10.66"
|
||||
|
||||
## some subdependencies require higher Rust version than 1.74 (our MSRV)
|
||||
## cargo update && cargo update ravif --precise 0.11.5 && cargo update bitstream-io --precise 2.3.0
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
Breaking changes are indicated by the :warning: icon.
|
||||
|
||||
## v20240725
|
||||
|
||||
- Added `i/export-followers` endpoint.
|
||||
|
||||
## v20240714
|
||||
|
||||
- The old Mastodon API has been replaced with a new implementation based on Iceshrimp’s.
|
||||
|
|
|
@ -5,6 +5,13 @@ Critical security updates are indicated by the :warning: icon.
|
|||
- 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.
|
||||
|
||||
## [v20240725](https://firefish.dev/firefish/firefish/-/merge_requests/11196/commits)
|
||||
|
||||
- Add followers list export feature
|
||||
- Add description about excluding conditions (e.g., 'firefish -info.firefish.dev', '(sleepy OR eepy) -morning') in post search
|
||||
- Technically this is not a new feature
|
||||
- Fix bugs
|
||||
|
||||
## [v20240714](https://firefish.dev/firefish/firefish/-/merge_requests/11146/commits)
|
||||
|
||||
- Mastodon API implementation was ported from Iceshrimp, with added Firefish extensions including push notifications, post languages, schedule post support, and more. (#10880)
|
||||
|
|
|
@ -6,6 +6,14 @@ You can skip intermediate versions when upgrading from an old version, but pleas
|
|||
|
||||
Please take a look at #10947.
|
||||
|
||||
## v20240725
|
||||
|
||||
### For LibreTranslate self-hosters
|
||||
|
||||
Previously, neither the DeepL API nor the LibreTranslate API provided traditional Chinese translations, so we used to provide traditional Chinese post translations using manual conversion from simplified Chinese translations.
|
||||
|
||||
However, now that LibreTranslate API supports traditional Chinese translations, we have removed the manual conversion process for LibreTranslate. So, if you are hosting your LibreTranslate instance, please ensure your LibreTranslate version is new enough to support traditional Chinese.
|
||||
|
||||
## v20240714
|
||||
|
||||
### For systemd/pm2 users
|
||||
|
|
|
@ -2240,12 +2240,14 @@ searchWordsDescription: "Per cercar publicacions, escriu el terme a buscar. Sepa
|
|||
les paraules amb espais per fer condicions AND o escriules dins de cometes per fer
|
||||
una cerca OR.\nPer exemple, 'dia nit' trobarà publicacions que continguin tan 'dia'
|
||||
com 'nit', i 'dia OR nit' trobara publicacions que continguin tant 'dia' com 'nit'
|
||||
(o ambdues).\nPots combinar condicions AND/OR per exemple '(dia OR nit) endormiscar'.\n
|
||||
Si vols cercar per una seqüencia de paraules (per exemple una frase) has d'escriure-les
|
||||
entre cometes dobles, per no fer una cerca amb condicionant AND: \"Avui he aprés\"\
|
||||
\n \nSi vols anar a una pàgina d'usuari o publicació en concret, escriu la adreça
|
||||
URL o la ID en aquest camp i fes clic al botó 'Ves a'. Fent clic a 'Cerca' trobarà
|
||||
publicacions que, literalment , continguin la ID/adreça URL."
|
||||
(o ambdues).\nPots filtrar certes paraules en els resultats de la cerca, com 'endormiscat
|
||||
-matí -esmorzar'. Encara més, pots combinar aquestes condicions AND/OR/exclude d'aquesta
|
||||
manera '(mati OR nit) endormiscat -esmorzar'.\n Si vols cercar per una seqüencia
|
||||
de paraules (per exemple una frase) has d'escriure-les entre cometes dobles, per
|
||||
no fer una cerca amb condicionant AND: \"Avui he aprés\"\n \nSi vols anar a una
|
||||
pàgina d'usuari o publicació en concret, escriu la adreça URL o la ID en aquest
|
||||
camp i fes clic al botó 'Ves a'. Fent clic a 'Cerca' trobarà publicacions que, literalment
|
||||
, continguin la ID/adreça URL."
|
||||
searchPostsWithFiles: Només publicacions amb fitxers
|
||||
searchCwAndAlt: Inclou avisos de contingut i arxius amb descripcions
|
||||
searchUsers: Publicat per (opcional)
|
||||
|
|
|
@ -1211,10 +1211,11 @@ 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 combine AND/OR conditions like '(morning OR
|
||||
night) sleepy'.\nIf you want to search for a sequence of words (e.g., a sentence),
|
||||
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\n If you want to go to a specific user page or post page, enter the ID or URL
|
||||
\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)"
|
||||
|
|
|
@ -2195,3 +2195,29 @@ squareCatAvatars: Mostrar avatares cuadrados para las cuentas de gatos
|
|||
useThisAccountConfirm: ¿Quieres continuar con esta cuenta?
|
||||
i18nServerInfo: Nuevos clientes estarán en {language} por defecto.
|
||||
media: Medios
|
||||
ipFirstAcknowledged: Fecha de la primera adquisición de la dirección IP
|
||||
driveCapacityOverride: Anulación de la capacidad de accionamiento
|
||||
useCdn: Obtener activos de CDN
|
||||
replaceChatButtonWithAccountButton: Reemplazar boton del chat con el boton de cambio
|
||||
de cuenta
|
||||
forMobile: Móvil
|
||||
emojiModPerm: Permiso de gestión de emoji personalizado
|
||||
replaceWidgetsButtonWithReloadButton: Reemplazar botón del los widgets con el boton
|
||||
de recarga
|
||||
inputAccountId: Introduce tu cuenta (ej., @firefish@info.firefish.dev)
|
||||
remoteFollow: Seguimiento remoto
|
||||
useCdnDescription: Cargar algunos activos estáticos como Twemoji desde JSDelivr CDN
|
||||
en lugar de este servidor de Firefish.
|
||||
suggested: Sugerido
|
||||
noLanguage: Ningún idioma
|
||||
showPreviewByDefault: Mostrar vista previa en el formulario de publicación por defecto
|
||||
preventMisclick: Prevención de clic accidental
|
||||
announcement: Anuncio
|
||||
moderationNote: Nota de moderación
|
||||
getQrCode: Mostrar código QR
|
||||
copyRemoteFollowUrl: Copiar URL de seguimiento remoto
|
||||
hideFollowButtons: Ocultar botón de seguimiento en una posición en la que se pueda
|
||||
hacer clic erróneamente
|
||||
searchEngine: Motor de búsqueda usado en la barra de búsqueda MFM
|
||||
postSearch: Búsqueda de publicaciones en este servidor
|
||||
showBigPostButton: Mostrar un gran botón de Publicar en el formulario de publicación
|
||||
|
|
|
@ -2222,11 +2222,12 @@ searchWordsDescription: "Masukkan kata kunci di sini untuk mencari postingan. Pi
|
|||
kata dengan spasi untuk pencarian AND (dan), atau 'OR' ('atau', tanpa tanda kutip)
|
||||
di antara kata-kata untuk pencarian OR.\nMisalnya, 'pagi malam' akan menemukan postingan
|
||||
yang mengandung 'pagi' dan 'malam', dan 'pagi OR malam' akan menemukan postingan
|
||||
yang mengandung 'pagi' atau 'malam' (atau keduanya).\nAnda juga dapat menggabungkan
|
||||
kondisi AND/OR seperti '(pagi OR malam) mengantuk'.\n\n Jika kamu ingin membuka
|
||||
halaman pengguna atau halaman postingan tertentu, masukkan ID atau URL pada kolom
|
||||
ini dan klik tombol 'Cari'. Mengeklik 'Cari' akan mencari postingan yang secara
|
||||
harfiah mengandung ID/URL."
|
||||
yang mengandung 'pagi' atau 'malam' (atau keduanya).\nAnda juga dapat memfilter
|
||||
kata tertentu dari hasil pencarian, seperti 'mengantuk -pagi -sarapan'. Selain itu,
|
||||
Anda dapat menggabungkan AND/OR/tidak sertakan ini seperti '(pagi OR malam) mengantuk'
|
||||
-sarapan.\n\n Jika kamu ingin membuka halaman pengguna atau halaman postingan tertentu,
|
||||
masukkan ID atau URL pada kolom ini dan klik tombol 'Cari'. Mengeklik 'Cari' akan
|
||||
mencari postingan yang secara harfiah mengandung ID/URL."
|
||||
pullToRefreshThreshold: Jarak penarikan untuk memuat ulang
|
||||
releaseToReload: Lepaskan untuk memuat ulang
|
||||
reloading: Memuat ulang
|
||||
|
|
|
@ -1011,10 +1011,10 @@ reloading: "読み込み中"
|
|||
enableTimelineStreaming: "タイムラインを自動で更新する"
|
||||
searchWords: "検索語句・照会するIDやURL"
|
||||
searchWordsDescription: "投稿を検索するには、ここに検索語句を入力してください。空白区切りでAND検索になり、ORを挟むとOR検索になります。\n
|
||||
例えば「朝 夜」と入力すると「朝」と「夜」が両方含まれた投稿を検索し、「朝 OR 夜」と入力すると「朝」または「夜」(または両方)が含まれた投稿を検索します。\n
|
||||
「(朝 OR 夜) 眠い」のように、AND検索とOR検索を同時に行うこともできます。\n空白を含む文字列をAND検索ではなくそのまま検索したい場合、\"明日 買うもの\"\
|
||||
\ のように二重引用符 (\") で囲む必要があります。\n\n特定のユーザーや投稿のページに飛びたい場合には、この欄にID (@user@example.com)
|
||||
や投稿のURLを入力し「照会」を押してください。「検索」を押すとそのIDやURLが文字通り含まれる投稿を検索します。"
|
||||
例えば「朝 夜」と入力すると「朝」と「夜」が両方含まれた投稿を検索し、「朝 OR 夜」と入力すると「朝」または「夜」(または両方)が含まれた投稿を検索します。また、「眠い
|
||||
-朝 -夜」のように特定の単語を除外した検索も可能です。\n 「(朝 OR 夜) 眠い -朝ごはん」のように、AND・OR・除外の条件を組み合わせることもできます。\n\
|
||||
空白を含む文字列をAND検索ではなくそのまま検索したい場合、\"明日 買うもの\" のように二重引用符 (\") で囲む必要があります。\n\n特定のユーザーや投稿のページに飛びたい場合には、この欄にID
|
||||
(@user@example.com) や投稿のURLを入力し「照会」を押してください。「検索」を押すとそのIDやURLが文字通り含まれる投稿を検索します。"
|
||||
searchUsers: "投稿元(省略可)"
|
||||
searchUsersDescription: "投稿検索で投稿者を絞りたい場合、@user@example.com(ローカルユーザーなら @user)の形式で投稿者のIDを入力してください。ユーザーIDではなくドメイン名
|
||||
(example.com) を指定すると、そのサーバーの投稿を検索します。\n\nme とだけ入力すると、自分の投稿を検索します。この検索結果には未収載・フォロワー限定・ダイレクト・秘密を含む全ての投稿が含まれます。\n
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"name": "firefish",
|
||||
"version": "20240714",
|
||||
"version": "20240725",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://firefish.dev/firefish/firefish.git"
|
||||
},
|
||||
"packageManager": "pnpm@9.5.0",
|
||||
"packageManager": "pnpm@9.6.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.10",
|
||||
"@types/node": "20.14.12",
|
||||
"execa": "9.3.0",
|
||||
"pnpm": "9.5.0"
|
||||
"pnpm": "9.6.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ cuid2 = { workspace = true }
|
|||
emojis = { 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"] }
|
||||
isahc = { workspace = true, features = ["http2", "text-decoding", "json"] }
|
||||
nom-exif = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
openssl = { workspace = true, features = ["vendored"] }
|
||||
|
@ -49,6 +49,7 @@ tracing-subscriber = { workspace = true, features = ["ansi"] }
|
|||
url = { workspace = true }
|
||||
urlencoding = { workspace = true }
|
||||
web-push = { workspace = true, features = ["isahc-client"] }
|
||||
zhconv = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = { workspace = true, features = ["std"] }
|
||||
|
|
36
packages/backend-rs/index.d.ts
vendored
36
packages/backend-rs/index.d.ts
vendored
|
@ -270,8 +270,6 @@ export declare function cpuInfo(): Cpu
|
|||
|
||||
export declare function cpuUsage(): number
|
||||
|
||||
export const DAY: number
|
||||
|
||||
export interface DbConfig {
|
||||
host: string
|
||||
port: number
|
||||
|
@ -378,17 +376,6 @@ export declare function fetchMeta(): Promise<Meta>
|
|||
/** Fetches and returns the NodeInfo (version 2.0) of a remote server. */
|
||||
export declare function fetchNodeinfo(host: string): Promise<Nodeinfo>
|
||||
|
||||
/**
|
||||
* List of file types allowed to be viewed directly in the browser
|
||||
*
|
||||
* Anything not included here will be responded as application/octet-stream
|
||||
* SVG is not allowed because it generates XSS (TODO: fix this and later allow it to be viewed directly)
|
||||
* * <https://github.com/sindresorhus/file-type/blob/main/supported.js>
|
||||
* * <https://github.com/sindresorhus/file-type/blob/main/core.js>
|
||||
* * <https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers>
|
||||
*/
|
||||
export const FILE_TYPE_BROWSERSAFE: string[]
|
||||
|
||||
export interface Following {
|
||||
id: string
|
||||
createdAt: DateTimeWithTimeZone
|
||||
|
@ -490,8 +477,6 @@ export interface Hashtag {
|
|||
attachedRemoteUsersCount: number
|
||||
}
|
||||
|
||||
export const HOUR: number
|
||||
|
||||
export interface IdConfig {
|
||||
length?: number
|
||||
fingerprint?: string
|
||||
|
@ -555,7 +540,7 @@ export type InternalActor = 'instance'|
|
|||
* `host` - punycoded instance host
|
||||
*
|
||||
* # Example
|
||||
* ```no_run
|
||||
* ```ignore
|
||||
* # use backend_rs::misc::check_server_block::is_allowed_server;
|
||||
* # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
* assert_eq!(true, is_allowed_server("allowed.com").await?);
|
||||
|
@ -575,7 +560,7 @@ export declare function isAllowedServer(host: string): Promise<boolean>
|
|||
* `host` - punycoded instance host
|
||||
*
|
||||
* # Example
|
||||
* ```no_run
|
||||
* ```ignore
|
||||
* # use backend_rs::misc::check_server_block::is_blocked_server;
|
||||
* # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
* assert_eq!(true, is_blocked_server("blocked.com").await?);
|
||||
|
@ -606,7 +591,7 @@ export declare function isSelfHost(host?: string | undefined | null): boolean
|
|||
* `host` - punycoded instance host
|
||||
*
|
||||
* # Example
|
||||
* ```no_run
|
||||
* ```ignore
|
||||
* # use backend_rs::misc::check_server_block::is_silenced_server;
|
||||
* # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
* assert_eq!(true, is_silenced_server("silenced.com").await?);
|
||||
|
@ -752,8 +737,6 @@ export interface Migrations {
|
|||
name: string
|
||||
}
|
||||
|
||||
export const MINUTE: number
|
||||
|
||||
export interface ModerationLog {
|
||||
id: string
|
||||
createdAt: DateTimeWithTimeZone
|
||||
|
@ -1223,8 +1206,6 @@ export interface ReplyMuting {
|
|||
/** Returns `true` if `src` does not contain suspicious characters like `%`. */
|
||||
export declare function safeForSql(src: string): boolean
|
||||
|
||||
export const SECOND: number
|
||||
|
||||
export declare function sendPushNotification(receiverUserId: string, kind: PushNotificationKind, content: any): Promise<void>
|
||||
|
||||
export interface ServerConfig {
|
||||
|
@ -1349,6 +1330,13 @@ export declare function toDbReaction(reaction?: string | undefined | null, host?
|
|||
|
||||
export declare function toPuny(host: string): string
|
||||
|
||||
export declare function translate(text: string, sourceLang: string | undefined | null, targetLang: string): Promise<Translation>
|
||||
|
||||
export interface Translation {
|
||||
sourceLang: string
|
||||
text: string
|
||||
}
|
||||
|
||||
export declare function unwatchNote(watcherId: string, noteId: string): Promise<void>
|
||||
|
||||
export declare function updateAntennaCache(): Promise<void>
|
||||
|
@ -1413,10 +1401,6 @@ export interface User {
|
|||
readCatLanguage: boolean
|
||||
}
|
||||
|
||||
export const USER_ACTIVE_THRESHOLD: number
|
||||
|
||||
export const USER_ONLINE_THRESHOLD: number
|
||||
|
||||
export type UserEmojiModPerm = 'add'|
|
||||
'full'|
|
||||
'mod'|
|
||||
|
|
|
@ -336,7 +336,7 @@ if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
|
|||
nativeBinding = require('./backend-rs.wasi.cjs')
|
||||
} catch (err) {
|
||||
if (process.env.NAPI_RS_FORCE_WASI) {
|
||||
console.error(err)
|
||||
loadErrors.push(err)
|
||||
}
|
||||
}
|
||||
if (!nativeBinding) {
|
||||
|
@ -344,7 +344,7 @@ if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
|
|||
nativeBinding = require('backend-rs-wasm32-wasi')
|
||||
} catch (err) {
|
||||
if (process.env.NAPI_RS_FORCE_WASI) {
|
||||
console.error(err)
|
||||
loadErrors.push(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -370,7 +370,6 @@ module.exports.countLocalUsers = nativeBinding.countLocalUsers
|
|||
module.exports.countReactions = nativeBinding.countReactions
|
||||
module.exports.cpuInfo = nativeBinding.cpuInfo
|
||||
module.exports.cpuUsage = nativeBinding.cpuUsage
|
||||
module.exports.DAY = nativeBinding.DAY
|
||||
module.exports.decodeReaction = nativeBinding.decodeReaction
|
||||
module.exports.DriveFileEvent = nativeBinding.DriveFileEvent
|
||||
module.exports.DriveFileUsageHint = nativeBinding.DriveFileUsageHint
|
||||
|
@ -378,7 +377,6 @@ module.exports.DriveFolderEvent = nativeBinding.DriveFolderEvent
|
|||
module.exports.extractHost = nativeBinding.extractHost
|
||||
module.exports.fetchMeta = nativeBinding.fetchMeta
|
||||
module.exports.fetchNodeinfo = nativeBinding.fetchNodeinfo
|
||||
module.exports.FILE_TYPE_BROWSERSAFE = nativeBinding.FILE_TYPE_BROWSERSAFE
|
||||
module.exports.formatMilliseconds = nativeBinding.formatMilliseconds
|
||||
module.exports.generateSecureRandomString = nativeBinding.generateSecureRandomString
|
||||
module.exports.generateUserToken = nativeBinding.generateUserToken
|
||||
|
@ -391,7 +389,6 @@ module.exports.getNoteSummary = nativeBinding.getNoteSummary
|
|||
module.exports.getTimestamp = nativeBinding.getTimestamp
|
||||
module.exports.greet = nativeBinding.greet
|
||||
module.exports.hashPassword = nativeBinding.hashPassword
|
||||
module.exports.HOUR = nativeBinding.HOUR
|
||||
module.exports.Inbound = nativeBinding.Inbound
|
||||
module.exports.initializeRustLogger = nativeBinding.initializeRustLogger
|
||||
module.exports.InternalActor = nativeBinding.InternalActor
|
||||
|
@ -408,7 +405,6 @@ module.exports.latestVersion = nativeBinding.latestVersion
|
|||
module.exports.loadConfig = nativeBinding.loadConfig
|
||||
module.exports.memoryUsage = nativeBinding.memoryUsage
|
||||
module.exports.metaToPugArgs = nativeBinding.metaToPugArgs
|
||||
module.exports.MINUTE = nativeBinding.MINUTE
|
||||
module.exports.MutedNoteReason = nativeBinding.MutedNoteReason
|
||||
module.exports.nodeinfo_2_0 = nativeBinding.nodeinfo_2_0
|
||||
module.exports.nodeinfo_2_1 = nativeBinding.nodeinfo_2_1
|
||||
|
@ -433,7 +429,6 @@ module.exports.PushSubscriptionType = nativeBinding.PushSubscriptionType
|
|||
module.exports.RelayStatus = nativeBinding.RelayStatus
|
||||
module.exports.removeOldAttestationChallenges = nativeBinding.removeOldAttestationChallenges
|
||||
module.exports.safeForSql = nativeBinding.safeForSql
|
||||
module.exports.SECOND = nativeBinding.SECOND
|
||||
module.exports.sendPushNotification = nativeBinding.sendPushNotification
|
||||
module.exports.shouldNyaify = nativeBinding.shouldNyaify
|
||||
module.exports.showServerInfo = nativeBinding.showServerInfo
|
||||
|
@ -443,13 +438,12 @@ module.exports.storageUsage = nativeBinding.storageUsage
|
|||
module.exports.stringToAcct = nativeBinding.stringToAcct
|
||||
module.exports.toDbReaction = nativeBinding.toDbReaction
|
||||
module.exports.toPuny = nativeBinding.toPuny
|
||||
module.exports.translate = nativeBinding.translate
|
||||
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.USER_ACTIVE_THRESHOLD = nativeBinding.USER_ACTIVE_THRESHOLD
|
||||
module.exports.USER_ONLINE_THRESHOLD = nativeBinding.USER_ONLINE_THRESHOLD
|
||||
module.exports.UserEmojiModPerm = nativeBinding.UserEmojiModPerm
|
||||
module.exports.UserProfileFfvisibility = nativeBinding.UserProfileFfvisibility
|
||||
module.exports.UserProfileMutingNotificationTypes = nativeBinding.UserProfileMutingNotificationTypes
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"binaryName": "backend-rs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@napi-rs/cli": "3.0.0-alpha.58"
|
||||
"@napi-rs/cli": "3.0.0-alpha.62"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "napi build --features napi --no-const-enum --platform --release --output-dir ./built/",
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
//! This module is used in the TypeScript backend only.
|
||||
|
||||
#[macros::ts_export]
|
||||
pub const SECOND: i32 = 1000;
|
||||
#[macros::ts_export]
|
||||
pub const MINUTE: i32 = 60 * SECOND;
|
||||
#[macros::ts_export]
|
||||
pub const HOUR: i32 = 60 * MINUTE;
|
||||
#[macros::ts_export]
|
||||
pub const DAY: i32 = 24 * HOUR;
|
||||
|
||||
#[macros::ts_export]
|
||||
pub const USER_ONLINE_THRESHOLD: i32 = 10 * MINUTE;
|
||||
#[macros::ts_export]
|
||||
pub const USER_ACTIVE_THRESHOLD: i32 = 3 * DAY;
|
||||
|
||||
/// List of file types allowed to be viewed directly in the browser
|
||||
///
|
||||
/// Anything not included here will be responded as application/octet-stream
|
||||
/// SVG is not allowed because it generates XSS (TODO: fix this and later allow it to be viewed directly)
|
||||
/// * <https://github.com/sindresorhus/file-type/blob/main/supported.js>
|
||||
/// * <https://github.com/sindresorhus/file-type/blob/main/core.js>
|
||||
/// * <https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers>
|
||||
#[macros::ts_export]
|
||||
pub const FILE_TYPE_BROWSERSAFE: [&str; 41] = [
|
||||
// Images
|
||||
"image/png",
|
||||
"image/gif", // TODO: deprecated, but still used by old posts, new gifs should be converted to webp in the future
|
||||
"image/jpeg",
|
||||
"image/webp", // TODO: make this the default image format
|
||||
"image/apng",
|
||||
"image/bmp",
|
||||
"image/tiff",
|
||||
"image/x-icon",
|
||||
"image/avif", // not as good supported now, but its good to introduce initial support for the future
|
||||
// OggS
|
||||
"audio/opus",
|
||||
"video/ogg",
|
||||
"audio/ogg",
|
||||
"application/ogg",
|
||||
// ISO/IEC base media file format
|
||||
"video/quicktime",
|
||||
"video/mp4", // TODO: we need to check for av1 later
|
||||
"video/vnd.avi", // also av1
|
||||
"audio/mp4",
|
||||
"video/x-m4v",
|
||||
"audio/x-m4a",
|
||||
"video/3gpp",
|
||||
"video/3gpp2",
|
||||
"video/3gp2",
|
||||
"audio/3gpp",
|
||||
"audio/3gpp2",
|
||||
"audio/3gp2",
|
||||
"video/mpeg",
|
||||
"audio/mpeg",
|
||||
"video/webm",
|
||||
"audio/webm",
|
||||
"audio/aac",
|
||||
"audio/x-flac",
|
||||
"audio/flac",
|
||||
"audio/vnd.wave",
|
||||
"audio/mod",
|
||||
"audio/x-mod",
|
||||
"audio/s3m",
|
||||
"audio/x-s3m",
|
||||
"audio/xm",
|
||||
"audio/x-xm",
|
||||
"audio/it",
|
||||
"audio/x-it",
|
||||
];
|
|
@ -3,6 +3,5 @@
|
|||
pub use meta::local_server_info;
|
||||
pub use server::CONFIG;
|
||||
|
||||
pub mod constant;
|
||||
pub mod meta;
|
||||
pub mod server;
|
||||
|
|
|
@ -14,7 +14,7 @@ pub enum Category {
|
|||
Test,
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[error("failed to execute Redis command")]
|
||||
Redis(#[from] RedisError),
|
||||
|
|
|
@ -82,7 +82,7 @@ async fn init_conn_pool() -> Result<(), RedisError> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum RedisConnError {
|
||||
#[error("failed to initialize Redis connection pool")]
|
||||
Redis(RedisError),
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{database::db_conn, model::entity::user};
|
|||
use sea_orm::prelude::*;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
#[doc = "database error"]
|
||||
|
|
|
@ -7,7 +7,7 @@ use isahc::AsyncReadResponseExt;
|
|||
use serde::Deserialize;
|
||||
|
||||
/// Errors that can occur while fetching NodeInfo from a remote server
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[error("failed to acquire an HTTP client")]
|
||||
HttpClient(#[from] http_client::Error),
|
||||
|
|
|
@ -152,8 +152,8 @@ pub async fn nodeinfo_2_0() -> Result<Nodeinfo20, DbErr> {
|
|||
Ok(nodeinfo_2_1().await?.into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi")]
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[cfg(any(test, doctest, feature = "napi"))]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[doc = "database error"]
|
||||
#[error(transparent)]
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
/// `host` - punycoded instance host
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// ```ignore
|
||||
/// # use backend_rs::misc::check_server_block::is_blocked_server;
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// assert_eq!(true, is_blocked_server("blocked.com").await?);
|
||||
|
@ -35,7 +35,7 @@ pub async fn is_blocked_server(host: &str) -> Result<bool, sea_orm::DbErr> {
|
|||
/// `host` - punycoded instance host
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// ```ignore
|
||||
/// # use backend_rs::misc::check_server_block::is_silenced_server;
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// assert_eq!(true, is_silenced_server("silenced.com").await?);
|
||||
|
@ -63,7 +63,7 @@ pub async fn is_silenced_server(host: &str) -> Result<bool, sea_orm::DbErr> {
|
|||
/// `host` - punycoded instance host
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// ```ignore
|
||||
/// # use backend_rs::misc::check_server_block::is_allowed_server;
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// assert_eq!(true, is_allowed_server("allowed.com").await?);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// We may want to (re)implement these functions in the `federation` module
|
||||
// in a Rusty way (e.g., traits of actor type) if needed.
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[doc = "UTS #46 process has failed"]
|
||||
#[error(transparent)]
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::{database::cache, util::http_client};
|
||||
use image::{io::Reader, ImageError, ImageFormat};
|
||||
use image::{ImageError, ImageFormat, ImageReader};
|
||||
use isahc::AsyncReadResponseExt;
|
||||
use nom_exif::{parse_jpeg_exif, EntryValue, ExifTag};
|
||||
use std::io::Cursor;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[error("Redis cache operation has failed")]
|
||||
Cache(#[from] cache::Error),
|
||||
|
@ -87,7 +87,7 @@ pub async fn get_image_size_from_url(url: &str) -> Result<ImageSize, Error> {
|
|||
|
||||
let image_bytes = response.bytes().await?;
|
||||
|
||||
let reader = Reader::new(Cursor::new(&image_bytes)).with_guessed_format()?;
|
||||
let reader = ImageReader::new(Cursor::new(&image_bytes)).with_guessed_format()?;
|
||||
|
||||
let format = reader.format();
|
||||
if format.is_none() || !BROWSER_SAFE_IMAGE_TYPES.contains(&format.unwrap()) {
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{database::cache, util::http_client};
|
|||
use isahc::AsyncReadResponseExt;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[error("Redis cache operation has failed")]
|
||||
Cache(#[from] cache::Error),
|
||||
|
|
|
@ -17,4 +17,5 @@ pub mod reaction;
|
|||
pub mod remove_old_attestation_challenges;
|
||||
pub mod should_nyaify;
|
||||
pub mod system_info;
|
||||
pub mod translate;
|
||||
pub mod user;
|
||||
|
|
|
@ -15,7 +15,7 @@ pub fn hash_password(password: &str) -> Result<String, password_hash::errors::Er
|
|||
.to_string())
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[error("failed to verify password against bcrypt hash")]
|
||||
Bcrypt(#[from] bcrypt::BcryptError),
|
||||
|
|
|
@ -53,7 +53,7 @@ pub fn count_reactions(reactions: &HashMap<String, u32>) -> HashMap<String, u32>
|
|||
res
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[doc = "UTS #46 process has failed"]
|
||||
#[error(transparent)]
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
};
|
||||
use sea_orm::{DbErr, EntityTrait, QuerySelect, SelectColumns};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[doc = "database error"]
|
||||
#[error(transparent)]
|
||||
|
|
272
packages/backend-rs/src/misc/translate.rs
Normal file
272
packages/backend-rs/src/misc/translate.rs
Normal file
|
@ -0,0 +1,272 @@
|
|||
use crate::{
|
||||
config::{local_server_info, server, CONFIG},
|
||||
util::http_client,
|
||||
};
|
||||
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[doc = "database error"]
|
||||
#[error(transparent)]
|
||||
Db(#[from] sea_orm::DbErr),
|
||||
#[error("failed to acquire an HTTP client")]
|
||||
HttpClient(#[from] http_client::Error),
|
||||
#[error("invalid http request body")]
|
||||
InvalidRequestBody(#[from] isahc::http::Error),
|
||||
#[error("http request failed")]
|
||||
HttpRequest(#[from] isahc::Error),
|
||||
#[error("failed to serialize the request body")]
|
||||
Serialize(#[from] serde_json::Error),
|
||||
#[error("Libretranslate API url is not set")]
|
||||
MissingApiUrl,
|
||||
#[error("DeepL API key is not set")]
|
||||
MissingApiKey,
|
||||
#[error("no response")]
|
||||
NoResponse,
|
||||
#[error("translator is not set")]
|
||||
NoTranslator,
|
||||
}
|
||||
|
||||
#[macros::export(object)]
|
||||
pub struct Translation {
|
||||
pub source_lang: String,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_zh_hant_tw(lang: &str) -> bool {
|
||||
["zh-tw", "zh-hant", "zh-hant-tw"].contains(&lang.to_ascii_lowercase().as_str())
|
||||
}
|
||||
|
||||
#[macros::export]
|
||||
pub async fn translate(
|
||||
text: &str,
|
||||
source_lang: Option<&str>,
|
||||
target_lang: &str,
|
||||
) -> Result<Translation, Error> {
|
||||
let config = local_server_info().await?;
|
||||
|
||||
let translation = if let Some(api_key) = config.deepl_auth_key {
|
||||
deepl_translate::translate(
|
||||
text,
|
||||
source_lang,
|
||||
target_lang,
|
||||
&api_key,
|
||||
config.deepl_is_pro,
|
||||
)
|
||||
.await?
|
||||
} else if let Some(api_url) = config.libre_translate_api_url {
|
||||
libre_translate::translate(
|
||||
text,
|
||||
source_lang,
|
||||
target_lang,
|
||||
&api_url,
|
||||
config.libre_translate_api_key.as_deref(),
|
||||
)
|
||||
.await?
|
||||
} else if let Some(server::DeepLConfig {
|
||||
auth_key, is_pro, ..
|
||||
}) = CONFIG.deepl.as_ref()
|
||||
{
|
||||
deepl_translate::translate(
|
||||
text,
|
||||
source_lang,
|
||||
target_lang,
|
||||
auth_key.as_ref().ok_or(Error::MissingApiKey)?,
|
||||
is_pro.unwrap_or(false),
|
||||
)
|
||||
.await?
|
||||
} else if let Some(server::LibreTranslateConfig {
|
||||
api_url, api_key, ..
|
||||
}) = CONFIG.libre_translate.as_ref()
|
||||
{
|
||||
libre_translate::translate(
|
||||
text,
|
||||
source_lang,
|
||||
target_lang,
|
||||
api_url.as_ref().ok_or(Error::MissingApiUrl)?,
|
||||
api_key.as_deref(),
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
return Err(Error::NoTranslator);
|
||||
};
|
||||
|
||||
Ok(translation)
|
||||
}
|
||||
|
||||
mod deepl_translate {
|
||||
use crate::util::http_client;
|
||||
use isahc::{AsyncReadResponseExt, Request};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Response {
|
||||
translations: Vec<Translation>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct Translation {
|
||||
detected_source_language: Option<String>,
|
||||
text: String,
|
||||
}
|
||||
|
||||
pub(super) async fn translate(
|
||||
text: &str,
|
||||
source_lang: Option<&str>,
|
||||
target_lang: &str,
|
||||
api_key: &str,
|
||||
is_pro: bool,
|
||||
) -> Result<super::Translation, super::Error> {
|
||||
let client = http_client::client()?;
|
||||
|
||||
let api_url = if is_pro {
|
||||
"https://api.deepl.com/v2/translate"
|
||||
} else {
|
||||
"https://api-free.deepl.com/v2/translate"
|
||||
};
|
||||
|
||||
let to_zh_hant_tw = super::is_zh_hant_tw(target_lang);
|
||||
|
||||
let mut target_lang = target_lang.split('-').collect::<Vec<&str>>()[0];
|
||||
|
||||
// DeepL API requires us to specify "en-US" or "en-GB" for English
|
||||
// translations ("en" does not work), so we need to address it
|
||||
if target_lang == "en" {
|
||||
target_lang = "en-US";
|
||||
}
|
||||
|
||||
let body = if let Some(source_lang) = source_lang {
|
||||
let source_lang = source_lang.split('-').collect::<Vec<&str>>()[0];
|
||||
|
||||
json!({
|
||||
"text": [text],
|
||||
"source_lang": source_lang,
|
||||
"target_lang": target_lang
|
||||
})
|
||||
} else {
|
||||
json!({
|
||||
"text": [text],
|
||||
"target_lang": target_lang
|
||||
})
|
||||
};
|
||||
|
||||
let request = Request::post(api_url)
|
||||
.header("Authorization", format!("DeepL-Auth-Key {}", api_key))
|
||||
.header("Content-Type", "application/json")
|
||||
.body(serde_json::to_string(&body)?)?;
|
||||
|
||||
let response = client.send_async(request).await?.json::<Response>().await?;
|
||||
|
||||
let result = response
|
||||
.translations
|
||||
.first()
|
||||
.ok_or(super::Error::NoResponse)?
|
||||
.to_owned();
|
||||
|
||||
let mut translation = super::Translation {
|
||||
source_lang: source_lang
|
||||
.map(|s| s.to_owned())
|
||||
.or(result.detected_source_language)
|
||||
.and_then(|lang| {
|
||||
if lang.is_ascii() {
|
||||
Some(lang.to_ascii_lowercase())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| "unknown".to_owned()),
|
||||
text: result.text,
|
||||
};
|
||||
|
||||
// DeepL translate don't provide zh-Hant-TW translations at this moment,
|
||||
// so we convert zh-Hans-CN translations into zh-Hant-TW using zhconv.
|
||||
if to_zh_hant_tw {
|
||||
translation.text = zhconv::zhconv(&translation.text, zhconv::Variant::ZhTW);
|
||||
}
|
||||
|
||||
Ok(translation)
|
||||
}
|
||||
}
|
||||
|
||||
mod libre_translate {
|
||||
use crate::util::http_client;
|
||||
use isahc::{AsyncReadResponseExt, Request};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct Translation {
|
||||
translated_text: String,
|
||||
detected_language: DetectedLanguage,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct DetectedLanguage {
|
||||
language: String,
|
||||
}
|
||||
|
||||
pub(super) async fn translate(
|
||||
text: &str,
|
||||
source_lang: Option<&str>,
|
||||
target_lang: &str,
|
||||
api_url: &str,
|
||||
api_key: Option<&str>,
|
||||
) -> Result<super::Translation, super::Error> {
|
||||
let client = http_client::client()?;
|
||||
|
||||
let target_lang = if super::is_zh_hant_tw(target_lang) {
|
||||
"zt"
|
||||
} else {
|
||||
target_lang.split('-').collect::<Vec<&str>>()[0]
|
||||
};
|
||||
|
||||
let body = if let Some(source_lang) = source_lang {
|
||||
let source_lang = source_lang.split('-').collect::<Vec<&str>>()[0];
|
||||
|
||||
json!({
|
||||
"q": [text],
|
||||
"source": source_lang,
|
||||
"target": target_lang,
|
||||
"format": "text",
|
||||
"alternatives": 1,
|
||||
"api_key": api_key.unwrap_or_default()
|
||||
})
|
||||
} else {
|
||||
json!({
|
||||
"q": [text],
|
||||
"source": "auto",
|
||||
"target": target_lang,
|
||||
"format": "text",
|
||||
"alternatives": 1,
|
||||
"api_key": api_key.unwrap_or_default()
|
||||
})
|
||||
};
|
||||
|
||||
let request = Request::post(api_url)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(serde_json::to_string(&body)?)?;
|
||||
|
||||
let result = client
|
||||
.send_async(request)
|
||||
.await?
|
||||
.json::<Translation>()
|
||||
.await?;
|
||||
|
||||
Ok(super::Translation {
|
||||
source_lang: source_lang
|
||||
.map(|s| s.to_owned())
|
||||
.or(Some(result.detected_language.language))
|
||||
.and_then(|lang| {
|
||||
if lang.is_ascii() {
|
||||
Some(lang.to_ascii_lowercase())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| "unknown".to_owned()),
|
||||
text: result.translated_text,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
};
|
||||
use sea_orm::{prelude::*, QuerySelect};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum AntennaCheckError {
|
||||
#[doc = "database error"]
|
||||
#[error(transparent)]
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::{
|
|||
use redis::{streams::StreamMaxlen, AsyncCommands, RedisError};
|
||||
use sea_orm::prelude::*;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[doc = "database error"]
|
||||
#[error(transparent)]
|
||||
|
|
|
@ -13,7 +13,7 @@ use sea_orm::prelude::*;
|
|||
use serde::Deserialize;
|
||||
use web_push::*;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[doc = "database error"]
|
||||
#[error(transparent)]
|
||||
|
|
|
@ -62,7 +62,7 @@ pub enum ChatEvent {
|
|||
Typing,
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[error("failed to execute a Redis command")]
|
||||
Redis(#[from] RedisError),
|
||||
|
|
|
@ -34,7 +34,7 @@ mod unit_test {
|
|||
#[error("unexpected string '{0}'")]
|
||||
struct InnerError2(String);
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
enum ErrorVariants {
|
||||
#[error("error 1 occured")]
|
||||
Error1(#[from] InnerError1),
|
||||
|
|
|
@ -5,7 +5,7 @@ use isahc::{config::*, HttpClient};
|
|||
use once_cell::sync::OnceCell;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[error("HTTP request failed")]
|
||||
Isahc(#[from] isahc::Error),
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"extension": ["ts","js","cjs","mjs"],
|
||||
"node-option": [
|
||||
"experimental-specifier-resolution=node",
|
||||
"loader=./test/loader.js"
|
||||
],
|
||||
"slow": 1000,
|
||||
"timeout": 30000,
|
||||
"exit": true
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import { loadConfig } from "./built/config.js";
|
||||
import { createRedisConnection } from "./built/redis.js";
|
||||
|
||||
const config = loadConfig();
|
||||
const redis = createRedisConnection(config);
|
||||
|
||||
redis.on("connect", () => redis.disconnect());
|
||||
redis.on("error", (e) => {
|
||||
throw e;
|
||||
});
|
|
@ -10,33 +10,30 @@
|
|||
"migration:run": "typeorm migration:run --dataSource ./built/ormconfig.js",
|
||||
"migration:revert": "typeorm migration:revert --dataSource ./built/ormconfig.js",
|
||||
"migration:new": "pnpm node ./scripts/create-migration.mjs",
|
||||
"check:connect": "node ./check_connect.js",
|
||||
"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",
|
||||
"mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
|
||||
"test": "pnpm run mocha",
|
||||
"format": "pnpm biome format * --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bull-board/api": "5.21.0",
|
||||
"@bull-board/koa": "5.21.0",
|
||||
"@bull-board/ui": "5.21.0",
|
||||
"@bull-board/api": "5.21.1",
|
||||
"@bull-board/koa": "5.21.1",
|
||||
"@bull-board/ui": "5.21.1",
|
||||
"@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.0",
|
||||
"@redocly/openapi-core": "1.18.1",
|
||||
"@sinonjs/fake-timers": "11.2.2",
|
||||
"adm-zip": "0.5.14",
|
||||
"ajv": "8.17.1",
|
||||
"archiver": "7.0.1",
|
||||
"async-lock": "1.4.1",
|
||||
"async-mutex": "0.5.0",
|
||||
"aws-sdk": "2.1659.0",
|
||||
"aws-sdk": "2.1662.0",
|
||||
"axios": "1.7.2",
|
||||
"backend-rs": "workspace:*",
|
||||
"blurhash": "2.0.5",
|
||||
|
@ -50,20 +47,19 @@
|
|||
"date-fns": "3.6.0",
|
||||
"decompress": "4.2.1",
|
||||
"deep-email-validator": "0.1.21",
|
||||
"deepl-node": "1.13.0",
|
||||
"escape-regexp": "0.0.1",
|
||||
"feed": "4.2.2",
|
||||
"file-type": "19.1.1",
|
||||
"file-type": "19.3.0",
|
||||
"firefish-js": "workspace:*",
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"form-data": "4.0.0",
|
||||
"got": "14.4.1",
|
||||
"got": "14.4.2",
|
||||
"gunzip-maybe": "1.4.2",
|
||||
"hpagent": "1.2.0",
|
||||
"ioredis": "5.4.1",
|
||||
"ip-cidr": "4.0.1",
|
||||
"is-svg": "5.0.1",
|
||||
"jsdom": "24.1.0",
|
||||
"jsdom": "24.1.1",
|
||||
"json5": "2.2.3",
|
||||
"jsonld": "8.3.2",
|
||||
"jsrsasign": "11.1.0",
|
||||
|
@ -79,12 +75,11 @@
|
|||
"koa-send": "5.0.1",
|
||||
"mfm-js": "0.24.0",
|
||||
"mime-types": "2.1.35",
|
||||
"msgpackr": "1.10.2",
|
||||
"msgpackr": "1.11.0",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"nested-property": "4.0.0",
|
||||
"node-fetch": "3.3.2",
|
||||
"nodemailer": "6.9.14",
|
||||
"opencc-js": "1.0.5",
|
||||
"otpauth": "9.3.1",
|
||||
"parse5": "7.1.2",
|
||||
"pg": "8.12.0",
|
||||
|
@ -103,7 +98,7 @@
|
|||
"rndstr": "1.0.0",
|
||||
"rss-parser": "3.13.0",
|
||||
"sanitize-html": "2.13.0",
|
||||
"semver": "7.6.2",
|
||||
"semver": "7.6.3",
|
||||
"sharp": "0.33.4",
|
||||
"stringz": "2.1.0",
|
||||
"summaly": "2.7.0",
|
||||
|
@ -140,14 +135,13 @@
|
|||
"@types/koa__cors": "5.0.0",
|
||||
"@types/koa__multer": "2.0.7",
|
||||
"@types/koa__router": "12.0.4",
|
||||
"@types/mocha": "10.0.7",
|
||||
"@types/node": "20.14.10",
|
||||
"@types/node": "20.14.12",
|
||||
"@types/node-fetch": "2.6.11",
|
||||
"@types/nodemailer": "6.4.15",
|
||||
"@types/oauth": "0.9.5",
|
||||
"@types/opencc-js": "1.0.3",
|
||||
"@types/pg": "8.11.6",
|
||||
"@types/probe-image-size": "7.2.4",
|
||||
"@types/probe-image-size": "7.2.5",
|
||||
"@types/pug": "2.0.10",
|
||||
"@types/punycode": "2.1.4",
|
||||
"@types/qrcode": "1.5.5",
|
||||
|
@ -165,15 +159,14 @@
|
|||
"@types/websocket": "1.0.10",
|
||||
"@types/ws": "8.5.11",
|
||||
"cross-env": "7.0.3",
|
||||
"mocha": "10.6.0",
|
||||
"pug": "3.0.3",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"ts-loader": "9.5.1",
|
||||
"ts-node": "10.9.2",
|
||||
"tsc-alias": "1.8.10",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"type-fest": "4.21.0",
|
||||
"typescript": "5.5.3",
|
||||
"type-fest": "4.23.0",
|
||||
"typescript": "5.5.4",
|
||||
"webpack": "5.93.0",
|
||||
"ws": "8.18.0"
|
||||
}
|
||||
|
|
98
packages/backend/src/const.ts
Normal file
98
packages/backend/src/const.ts
Normal file
|
@ -0,0 +1,98 @@
|
|||
import { config } from "@/config.js";
|
||||
|
||||
// If you change DB_* values, you must also change the DB schema.
|
||||
/**
|
||||
* Maximum note text length that can be stored in DB.
|
||||
* Surrogate pairs count as one
|
||||
*
|
||||
* NOTE: this can hypothetically be pushed further
|
||||
* (up to 250000000), but will likely cause truncations
|
||||
* and incompatibilities with other servers,
|
||||
* as well as potential performance issues.
|
||||
*/
|
||||
export const DB_MAX_NOTE_TEXT_LENGTH = 100000;
|
||||
|
||||
/**
|
||||
* Maximum image description length that can be stored in DB.
|
||||
* Surrogate pairs count as one
|
||||
*/
|
||||
export const DB_MAX_IMAGE_COMMENT_LENGTH = 8192;
|
||||
|
||||
|
||||
export const MAX_NOTE_TEXT_LENGTH = Math.min(
|
||||
config.maxNoteLength ?? 3000,
|
||||
DB_MAX_NOTE_TEXT_LENGTH,
|
||||
);
|
||||
export const MAX_CAPTION_TEXT_LENGTH = Math.min(
|
||||
config.maxCaptionLength ?? 1500,
|
||||
DB_MAX_IMAGE_COMMENT_LENGTH,
|
||||
);
|
||||
|
||||
export const SECOND = 1000;
|
||||
export const MINUTE = 60 * SECOND;
|
||||
export const HOUR = 60 * MINUTE;
|
||||
export const DAY = 24 * HOUR;
|
||||
|
||||
export const USER_ONLINE_THRESHOLD = 10 * MINUTE;
|
||||
export const USER_ACTIVE_THRESHOLD = 3 * DAY;
|
||||
|
||||
// List of file types allowed to be viewed directly in the browser
|
||||
// Anything not included here will be responded as application/octet-stream
|
||||
// SVG is not allowed because it generates XSS <- we need to fix this and later allow it to be viewed directly
|
||||
export const FILE_TYPE_BROWSERSAFE = [
|
||||
// Images
|
||||
"image/png",
|
||||
"image/gif", // TODO: deprecated, but still used by old notes, new gifs should be converted to webp in the future
|
||||
"image/jpeg",
|
||||
"image/webp", // TODO: make this the default image format
|
||||
"image/apng",
|
||||
"image/bmp",
|
||||
"image/tiff",
|
||||
"image/x-icon",
|
||||
"image/avif", // not as good supported now, but its good to introduce initial support for the future
|
||||
|
||||
// OggS
|
||||
"audio/opus",
|
||||
"video/ogg",
|
||||
"audio/ogg",
|
||||
"application/ogg",
|
||||
|
||||
// ISO/IEC base media file format
|
||||
"video/quicktime",
|
||||
"video/mp4", // TODO: we need to check for av1 later
|
||||
"video/vnd.avi", // also av1
|
||||
"audio/mp4",
|
||||
"video/x-m4v",
|
||||
"audio/x-m4a",
|
||||
"video/3gpp",
|
||||
"video/3gpp2",
|
||||
"video/3gp2",
|
||||
"audio/3gpp",
|
||||
"audio/3gpp2",
|
||||
"audio/3gp2",
|
||||
|
||||
"video/mpeg",
|
||||
"audio/mpeg",
|
||||
|
||||
"video/webm",
|
||||
"audio/webm",
|
||||
|
||||
"audio/aac",
|
||||
"audio/x-flac",
|
||||
"audio/flac",
|
||||
"audio/vnd.wave",
|
||||
|
||||
"audio/mod",
|
||||
"audio/x-mod",
|
||||
"audio/s3m",
|
||||
"audio/x-s3m",
|
||||
"audio/xm",
|
||||
"audio/x-xm",
|
||||
"audio/it",
|
||||
"audio/x-it",
|
||||
];
|
||||
/*
|
||||
https://github.com/sindresorhus/file-type/blob/main/supported.js
|
||||
https://github.com/sindresorhus/file-type/blob/main/core.js
|
||||
https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
|
||||
*/
|
|
@ -209,7 +209,7 @@ export const db = new DataSource({
|
|||
family: config.redis.family == null ? 0 : config.redis.family,
|
||||
username: config.redis.user ?? "default",
|
||||
password: config.redis.pass,
|
||||
keyPrefix: `${config.redis.prefix}:query:`,
|
||||
keyPrefix: `${config.redisKeyPrefix}:query:`,
|
||||
db: config.redis.db || 0,
|
||||
tls: config.redis.tls,
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { FILE_TYPE_BROWSERSAFE } from "backend-rs";
|
||||
import { FILE_TYPE_BROWSERSAFE } from "@/const.js";
|
||||
|
||||
const dictionary = {
|
||||
"safe-file": FILE_TYPE_BROWSERSAFE,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Brackets } from "typeorm";
|
||||
import { isBlockedServer, DAY } from "backend-rs";
|
||||
import { isBlockedServer } from "backend-rs";
|
||||
import { DAY } from "@/const.js";
|
||||
import { Instances } from "@/models/index.js";
|
||||
import type { Instance } from "@/models/entities/instance.js";
|
||||
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
import fetch from "node-fetch";
|
||||
import { Converter } from "opencc-js";
|
||||
import { getAgentByUrl } from "@/misc/fetch.js";
|
||||
import { fetchMeta } from "backend-rs";
|
||||
import type { PostLanguage } from "firefish-js";
|
||||
import * as deepl from "deepl-node";
|
||||
|
||||
// DeepL translate and LibreTranslate don't provide
|
||||
// zh-Hant-TW translations, so we convert zh-Hans-CN
|
||||
// translations into zh-Hant-TW using opencc-js.
|
||||
function convertChinese(convert: boolean, src: string) {
|
||||
if (!convert) return src;
|
||||
const converter = Converter({ from: "cn", to: "twp" });
|
||||
return converter(src);
|
||||
}
|
||||
|
||||
function stem(lang: PostLanguage): string {
|
||||
let toReturn = lang as string;
|
||||
if (toReturn.includes("-")) toReturn = toReturn.split("-")[0];
|
||||
if (toReturn.includes("_")) toReturn = toReturn.split("_")[0];
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
export async function translate(
|
||||
text: string,
|
||||
from: PostLanguage | null,
|
||||
to: PostLanguage,
|
||||
) {
|
||||
const instance = await fetchMeta();
|
||||
|
||||
if (instance.deeplAuthKey == null && instance.libreTranslateApiUrl == null) {
|
||||
throw Error("No translator is set up on this server.");
|
||||
}
|
||||
|
||||
const source = from == null ? null : stem(from);
|
||||
const target = stem(to);
|
||||
|
||||
if (instance.libreTranslateApiUrl != null) {
|
||||
const jsonBody = {
|
||||
q: text,
|
||||
source: source ?? "auto",
|
||||
target,
|
||||
format: "text",
|
||||
api_key: instance.libreTranslateApiKey ?? "",
|
||||
};
|
||||
|
||||
const url = new URL(instance.libreTranslateApiUrl);
|
||||
if (url.pathname.endsWith("/")) {
|
||||
url.pathname = url.pathname.slice(0, -1);
|
||||
}
|
||||
if (!url.pathname.endsWith("/translate")) {
|
||||
url.pathname += "/translate";
|
||||
}
|
||||
const res = await fetch(url.toString(), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(jsonBody),
|
||||
agent: getAgentByUrl,
|
||||
});
|
||||
|
||||
const json = (await res.json()) as {
|
||||
detectedLanguage?: {
|
||||
confidence: number;
|
||||
language: string;
|
||||
};
|
||||
translatedText: string;
|
||||
};
|
||||
|
||||
return {
|
||||
sourceLang: source ?? json.detectedLanguage?.language,
|
||||
text: convertChinese(
|
||||
["zh-hant", "zh-TW"].includes(to),
|
||||
json.translatedText,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
const deeplTranslator = new deepl.Translator(instance.deeplAuthKey ?? "");
|
||||
const result = await deeplTranslator.translateText(
|
||||
text,
|
||||
source as deepl.SourceLanguageCode | null,
|
||||
// DeepL API requires us to specify "en-US" or "en-GB" for English
|
||||
// translations ("en" does not work), so we need to address it
|
||||
(target === "en" ? to : target) as deepl.TargetLanguageCode,
|
||||
);
|
||||
|
||||
return {
|
||||
sourceLang: source ?? result.detectedSourceLang,
|
||||
text: convertChinese(["zh-hant", "zh-TW"].includes(to), result.text),
|
||||
};
|
||||
}
|
|
@ -7,7 +7,7 @@ import type { Packed } from "@/misc/schema.js";
|
|||
import type { Promiseable } from "@/prelude/await-all.js";
|
||||
import { awaitAll } from "@/prelude/await-all.js";
|
||||
import { populateEmojis } from "@/misc/populate-emojis.js";
|
||||
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from "backend-rs";
|
||||
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from "@/const.js";
|
||||
import { Cache } from "@/misc/cache.js";
|
||||
import { db } from "@/db/postgre.js";
|
||||
import { isActor, getApId } from "@/remote/activitypub/type.js";
|
||||
|
|
|
@ -256,6 +256,25 @@ export function createExportFollowingJob(
|
|||
);
|
||||
}
|
||||
|
||||
export function createExportFollowersJob(
|
||||
user: ThinUser,
|
||||
excludeMuting = false,
|
||||
excludeInactive = false,
|
||||
) {
|
||||
return dbQueue.add(
|
||||
"exportFollowers",
|
||||
{
|
||||
user: user,
|
||||
excludeMuting,
|
||||
excludeInactive,
|
||||
},
|
||||
{
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function createExportMuteJob(user: ThinUser) {
|
||||
return dbQueue.add(
|
||||
"exportMute",
|
||||
|
|
115
packages/backend/src/queue/processors/db/export-followers.ts
Normal file
115
packages/backend/src/queue/processors/db/export-followers.ts
Normal file
|
@ -0,0 +1,115 @@
|
|||
import type Bull from "bull";
|
||||
import * as fs from "node:fs";
|
||||
|
||||
import { queueLogger } from "../../logger.js";
|
||||
import { addFile } from "@/services/drive/add-file.js";
|
||||
import { format as dateFormat } from "date-fns";
|
||||
import { getFullApAccount } from "backend-rs";
|
||||
import { createTemp } from "@/misc/create-temp.js";
|
||||
import { Users, Followings, Mutings } from "@/models/index.js";
|
||||
import { In, MoreThan, Not } from "typeorm";
|
||||
import type { DbUserJobData } from "@/queue/types.js";
|
||||
import type { Following } from "@/models/entities/following.js";
|
||||
import { inspect } from "node:util";
|
||||
|
||||
const logger = queueLogger.createSubLogger("export-followers");
|
||||
|
||||
export async function exportFollowers(
|
||||
job: Bull.Job<DbUserJobData>,
|
||||
done: () => void,
|
||||
): Promise<void> {
|
||||
logger.info(`Exporting followers of ${job.data.user.id} ...`);
|
||||
|
||||
const user = await Users.findOneBy({ id: job.data.user.id });
|
||||
if (user == null) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create temp file
|
||||
const [path, cleanup] = await createTemp();
|
||||
|
||||
logger.info(`temp file created: ${path}`);
|
||||
|
||||
try {
|
||||
const stream = fs.createWriteStream(path, { flags: "a" });
|
||||
|
||||
let cursor: Following["id"] | null = null;
|
||||
|
||||
const mutings = job.data.excludeMuting
|
||||
? await Mutings.findBy({
|
||||
muterId: user.id,
|
||||
})
|
||||
: [];
|
||||
|
||||
while (true) {
|
||||
const followers = (await Followings.find({
|
||||
where: {
|
||||
followeeId: user.id,
|
||||
...(mutings.length > 0
|
||||
? { followerId: Not(In(mutings.map((x) => x.muteeId))) }
|
||||
: {}),
|
||||
...(cursor ? { id: MoreThan(cursor) } : {}),
|
||||
},
|
||||
take: 100,
|
||||
order: {
|
||||
id: 1,
|
||||
},
|
||||
})) as Following[];
|
||||
|
||||
if (followers.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
cursor = followers[followers.length - 1].id;
|
||||
|
||||
for (const follower of followers) {
|
||||
const u = await Users.findOneBy({ id: follower.followerId });
|
||||
if (u == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
job.data.excludeInactive &&
|
||||
u.updatedAt &&
|
||||
Date.now() - u.updatedAt.getTime() > 1000 * 60 * 60 * 24 * 90
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const content = getFullApAccount(u.username, u.host);
|
||||
await new Promise<void>((res, rej) => {
|
||||
stream.write(`${content}\n`, (err) => {
|
||||
if (err) {
|
||||
logger.warn(`failed to export followers of ${job.data.user.id}`);
|
||||
logger.info(inspect(err));
|
||||
rej(err);
|
||||
} else {
|
||||
res();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
stream.end();
|
||||
logger.info(`Exported to: ${path}`);
|
||||
|
||||
const fileName = `followers-${dateFormat(
|
||||
new Date(),
|
||||
"yyyy-MM-dd-HH-mm-ss",
|
||||
)}.csv`;
|
||||
const driveFile = await addFile({
|
||||
user,
|
||||
path,
|
||||
name: fileName,
|
||||
force: true,
|
||||
});
|
||||
|
||||
logger.info(`Exported to: ${driveFile.id}`);
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
|
@ -3,6 +3,7 @@ import type { DbJobData } from "@/queue/types.js";
|
|||
import { deleteDriveFiles } from "./delete-drive-files.js";
|
||||
import { exportCustomEmojis } from "./export-custom-emojis.js";
|
||||
import { exportNotes } from "./export-notes.js";
|
||||
import { exportFollowers } from "./export-followers.js";
|
||||
import { exportFollowing } from "./export-following.js";
|
||||
import { exportMute } from "./export-mute.js";
|
||||
import { exportBlocking } from "./export-blocking.js";
|
||||
|
@ -22,6 +23,7 @@ const jobs = {
|
|||
deleteDriveFiles,
|
||||
exportCustomEmojis,
|
||||
exportNotes,
|
||||
exportFollowers,
|
||||
exportFollowing,
|
||||
exportMute,
|
||||
exportBlocking,
|
||||
|
|
|
@ -202,7 +202,7 @@ export default async (
|
|||
.finally(() => {
|
||||
const after = performance.now();
|
||||
const time = after - before;
|
||||
if (time > 1000) {
|
||||
if (time > 2000) {
|
||||
apiLogger.warn(`SLOW API CALL DETECTED: ${ep.name} (${time}ms)`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -174,6 +174,7 @@ import * as ep___i_authorizedApps from "./endpoints/i/authorized-apps.js";
|
|||
import * as ep___i_changePassword from "./endpoints/i/change-password.js";
|
||||
import * as ep___i_deleteAccount from "./endpoints/i/delete-account.js";
|
||||
import * as ep___i_exportBlocking from "./endpoints/i/export-blocking.js";
|
||||
import * as ep___i_exportFollowers from "./endpoints/i/export-followers.js";
|
||||
import * as ep___i_exportFollowing from "./endpoints/i/export-following.js";
|
||||
import * as ep___i_exportMute from "./endpoints/i/export-mute.js";
|
||||
import * as ep___i_exportNotes from "./endpoints/i/export-notes.js";
|
||||
|
@ -523,6 +524,7 @@ const eps = [
|
|||
["i/change-password", ep___i_changePassword],
|
||||
["i/delete-account", ep___i_deleteAccount],
|
||||
["i/export-blocking", ep___i_exportBlocking],
|
||||
["i/export-followers", ep___i_exportFollowers],
|
||||
["i/export-following", ep___i_exportFollowing],
|
||||
["i/export-mute", ep___i_exportMute],
|
||||
["i/export-notes", ep___i_exportNotes],
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import Resolver from "@/remote/activitypub/resolver.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["federation"],
|
||||
|
|
|
@ -4,7 +4,8 @@ import { createNote } from "@/remote/activitypub/models/note.js";
|
|||
import DbResolver from "@/remote/activitypub/db-resolver.js";
|
||||
import Resolver from "@/remote/activitypub/resolver.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { MINUTE, extractHost, isBlockedServer } from "backend-rs";
|
||||
import { MINUTE } from "@/const.js";
|
||||
import { extractHost, isBlockedServer } from "backend-rs";
|
||||
import { Users, Notes } from "@/models/index.js";
|
||||
import type { Note } from "@/models/entities/note.js";
|
||||
import type { CacheableLocalUser, User } from "@/models/entities/user.js";
|
||||
|
|
|
@ -3,7 +3,7 @@ import define from "@/server/api/define.js";
|
|||
import { ApiError } from "@/server/api/error.js";
|
||||
import { getUser } from "@/server/api/common/getters.js";
|
||||
import { Blockings, NoteWatchings, Users } from "@/models/index.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["account"],
|
||||
|
|
|
@ -3,7 +3,7 @@ import define from "@/server/api/define.js";
|
|||
import { ApiError } from "@/server/api/error.js";
|
||||
import { getUser } from "@/server/api/common/getters.js";
|
||||
import { Blockings, Users } from "@/models/index.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["account"],
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Emojis } from "@/models/index.js";
|
||||
import type { Emoji } from "@/models/entities/emoji.js";
|
||||
import { IsNull, In } from "typeorm";
|
||||
import { FILE_TYPE_BROWSERSAFE } from "backend-rs";
|
||||
import { FILE_TYPE_BROWSERSAFE } from "@/const.js";
|
||||
import define from "@/server/api/define.js";
|
||||
|
||||
export const meta = {
|
||||
|
|
|
@ -2,7 +2,8 @@ import { addFile } from "@/services/drive/add-file.js";
|
|||
import { DriveFiles } from "@/models/index.js";
|
||||
import { config } from "@/config.js";
|
||||
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
||||
import { MINUTE, fetchMeta } from "backend-rs";
|
||||
import { fetchMeta } from "backend-rs";
|
||||
import { MINUTE } from "@/const.js";
|
||||
import define from "@/server/api/define.js";
|
||||
import { apiLogger } from "@/server/api/logger.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
|
|
|
@ -2,7 +2,7 @@ import { uploadFromUrl } from "@/services/drive/upload-from-url.js";
|
|||
import define from "@/server/api/define.js";
|
||||
import { DriveFiles } from "@/models/index.js";
|
||||
import { publishMainStream } from "@/services/stream.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["drive"],
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { createExportCustomEmojisJob } from "@/queue/index.js";
|
||||
import define from "@/server/api/define.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
|
|
|
@ -4,7 +4,7 @@ import { ApiError } from "@/server/api/error.js";
|
|||
import { getUser } from "@/server/api/common/getters.js";
|
||||
import { Followings, Users } from "@/models/index.js";
|
||||
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["following", "users"],
|
||||
|
|
|
@ -3,7 +3,7 @@ import define from "@/server/api/define.js";
|
|||
import { ApiError } from "@/server/api/error.js";
|
||||
import { getUser } from "@/server/api/common/getters.js";
|
||||
import { Followings, Users } from "@/models/index.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["following", "users"],
|
||||
|
|
|
@ -3,7 +3,7 @@ import define from "@/server/api/define.js";
|
|||
import { ApiError } from "@/server/api/error.js";
|
||||
import { getUser } from "@/server/api/common/getters.js";
|
||||
import { Followings, Users } from "@/models/index.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["following", "users"],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { DriveFiles, GalleryPosts } from "@/models/index.js";
|
||||
import { HOUR, genIdAt } from "backend-rs";
|
||||
import { genIdAt } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
import { GalleryPost } from "@/models/entities/gallery-post.js";
|
||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { DriveFiles, GalleryPosts } from "@/models/index.js";
|
||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["gallery"],
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { MoreThan } from "typeorm";
|
||||
import { USER_ONLINE_THRESHOLD } from "backend-rs";
|
||||
import { USER_ONLINE_THRESHOLD } from "@/const.js";
|
||||
import { Users } from "@/models/index.js";
|
||||
import define from "@/server/api/define.js";
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { createExportBlockingJob } from "@/queue/index.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { createExportFollowersJob } from "@/queue/index.js";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
requireCredential: true,
|
||||
limit: {
|
||||
duration: HOUR,
|
||||
max: 1,
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
excludeMuting: { type: "boolean", default: false },
|
||||
excludeInactive: { type: "boolean", default: false },
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
||||
export default define(meta, paramDef, async (ps, user) => {
|
||||
createExportFollowersJob(user, ps.excludeMuting, ps.excludeInactive);
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { createExportFollowingJob } from "@/queue/index.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { createExportMuteJob } from "@/queue/index.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { createExportNotesJob } from "@/queue/index.js";
|
||||
import { DAY } from "backend-rs";
|
||||
import { DAY } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { createExportUserListsJob } from "@/queue/index.js";
|
||||
import { MINUTE } from "backend-rs";
|
||||
import { MINUTE } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
|
|
|
@ -2,7 +2,7 @@ import define from "@/server/api/define.js";
|
|||
import { createImportBlockingJob } from "@/queue/index.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { DriveFiles } from "@/models/index.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
|
|
|
@ -2,7 +2,7 @@ import define from "@/server/api/define.js";
|
|||
import { createImportFollowingJob } from "@/queue/index.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { DriveFiles } from "@/models/index.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
|
|
|
@ -2,7 +2,7 @@ import define from "@/server/api/define.js";
|
|||
import { createImportMutingJob } from "@/queue/index.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { DriveFiles } from "@/models/index.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
|
|
|
@ -2,7 +2,8 @@ import define from "@/server/api/define.js";
|
|||
import { createImportPostsJob } from "@/queue/index.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { DriveFiles } from "@/models/index.js";
|
||||
import { fetchMeta, DAY } from "backend-rs";
|
||||
import { fetchMeta } from "backend-rs";
|
||||
import { DAY } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
|
|
|
@ -2,7 +2,7 @@ import define from "@/server/api/define.js";
|
|||
import { createImportUserListsJob } from "@/queue/index.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { DriveFiles } from "@/models/index.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
|
|
|
@ -4,7 +4,8 @@ import { resolveUser } from "@/remote/resolve-user.js";
|
|||
import acceptAllFollowRequests from "@/services/following/requests/accept-all.js";
|
||||
import { publishToFollowers } from "@/services/i/update.js";
|
||||
import { publishMainStream } from "@/services/stream.js";
|
||||
import { stringToAcct, DAY } from "backend-rs";
|
||||
import { stringToAcct } from "backend-rs";
|
||||
import { DAY } from "@/const.js";
|
||||
import { apiLogger } from "@/server/api/logger.js";
|
||||
import define from "@/server/api/define.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { User } from "@/models/entities/user.js";
|
||||
import { resolveUser } from "@/remote/resolve-user.js";
|
||||
import { stringToAcct, DAY } from "backend-rs";
|
||||
import { stringToAcct } from "backend-rs";
|
||||
import { DAY } from "@/const.js";
|
||||
import DeliverManager from "@/remote/activitypub/deliver-manager.js";
|
||||
import { renderActivity } from "@/remote/activitypub/renderer/index.js";
|
||||
import define from "@/server/api/define.js";
|
||||
|
|
|
@ -6,7 +6,8 @@ import { Users, UserProfiles } from "@/models/index.js";
|
|||
import { sendEmail } from "@/services/send-email.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { validateEmailForAccount } from "@/services/validate-email-for-account.js";
|
||||
import { HOUR, verifyPassword } from "backend-rs";
|
||||
import { verifyPassword } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
|
|
|
@ -2,7 +2,7 @@ import define from "@/server/api/define.js";
|
|||
import { ApiError } from "@/server/api/error.js";
|
||||
import { MessagingMessages } from "@/models/index.js";
|
||||
import { deleteMessage } from "@/services/messages/delete.js";
|
||||
import { SECOND, HOUR } from "backend-rs";
|
||||
import { SECOND, HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["messaging"],
|
||||
|
|
|
@ -15,7 +15,7 @@ import { config } from "@/config.js";
|
|||
import { noteVisibilities } from "@/types.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import define from "@/server/api/define.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
import { getNote } from "@/server/api/common/getters.js";
|
||||
import { langmap } from "firefish-js";
|
||||
import { createScheduledNoteJob } from "@/queue/index.js";
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Users } from "@/models/index.js";
|
|||
import define from "@/server/api/define.js";
|
||||
import { getNote } from "@/server/api/common/getters.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { SECOND, HOUR } from "backend-rs";
|
||||
import { SECOND, HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["notes"],
|
||||
|
|
|
@ -18,7 +18,8 @@ import { config } from "@/config.js";
|
|||
import { noteVisibilities } from "@/types.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import define from "@/server/api/define.js";
|
||||
import { genId, HOUR } from "backend-rs";
|
||||
import { genId } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
import { getNote } from "@/server/api/common/getters.js";
|
||||
import { Poll } from "@/models/entities/poll.js";
|
||||
import * as mfm from "mfm-js";
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Notes } from "@/models/index.js";
|
|||
import define from "@/server/api/define.js";
|
||||
import { getNote } from "@/server/api/common/getters.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { SECOND, HOUR } from "backend-rs";
|
||||
import { SECOND, HOUR } from "@/const.js";
|
||||
import { publishNoteStream } from "@/services/stream.js";
|
||||
|
||||
export const meta = {
|
||||
|
|
|
@ -2,7 +2,7 @@ import deleteReaction from "@/services/note/reaction/delete.js";
|
|||
import define from "@/server/api/define.js";
|
||||
import { getNote } from "@/server/api/common/getters.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { SECOND, HOUR } from "backend-rs";
|
||||
import { SECOND, HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["reactions", "notes"],
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { ApiError } from "@/server/api/error.js";
|
||||
import { getNote } from "@/server/api/common/getters.js";
|
||||
import { translate } from "@/misc/translate.js";
|
||||
import type { PostLanguage } from "firefish-js";
|
||||
import { translate } from "backend-rs";
|
||||
import define from "@/server/api/define.js";
|
||||
|
||||
export const meta = {
|
||||
|
@ -47,7 +46,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
|
||||
return translate(
|
||||
note.text,
|
||||
note.lang as PostLanguage | null,
|
||||
ps.targetLang as PostLanguage,
|
||||
note.lang as string | null,
|
||||
ps.targetLang,
|
||||
);
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Notes, Users } from "@/models/index.js";
|
|||
import define from "@/server/api/define.js";
|
||||
import { getNote } from "@/server/api/common/getters.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { SECOND, HOUR } from "backend-rs";
|
||||
import { SECOND, HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["notes"],
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Pages, DriveFiles } from "@/models/index.js";
|
||||
import { genIdAt, HOUR } from "backend-rs";
|
||||
import { genIdAt } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
import { Page } from "@/models/entities/page.js";
|
||||
import define from "@/server/api/define.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Not } from "typeorm";
|
|||
import { Pages, DriveFiles } from "@/models/index.js";
|
||||
import define from "@/server/api/define.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { HOUR } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["pages"],
|
||||
|
|
|
@ -3,7 +3,8 @@ import { IsNull } from "typeorm";
|
|||
import { config } from "@/config.js";
|
||||
import { Users, UserProfiles, PasswordResetRequests } from "@/models/index.js";
|
||||
import { sendEmail } from "@/services/send-email.js";
|
||||
import { HOUR, genIdAt } from "backend-rs";
|
||||
import { genIdAt } from "backend-rs";
|
||||
import { HOUR } from "@/const.js";
|
||||
import define from "@/server/api/define.js";
|
||||
|
||||
export const meta = {
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
generateBlockedUserQuery,
|
||||
generateBlockQueryForUsers,
|
||||
} from "@/server/api/common/generate-block-query.js";
|
||||
import { DAY } from "backend-rs";
|
||||
import { DAY } from "@/const.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["users"],
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { config } from "@/config.js";
|
||||
import { FILE_TYPE_BROWSERSAFE } from "backend-rs";
|
||||
import { FILE_TYPE_BROWSERSAFE } from "@/const.js";
|
||||
import { countLocalUsers, fetchMeta } from "backend-rs";
|
||||
import {
|
||||
AnnouncementReads,
|
||||
|
|
|
@ -40,7 +40,7 @@ import {
|
|||
getStubMastoContext,
|
||||
type MastoContext,
|
||||
} from "@/server/api/mastodon/index.js";
|
||||
import { translate } from "@/misc/translate.js";
|
||||
import { translate } from "backend-rs";
|
||||
import { createScheduledNoteJob } from "@/queue/index.js";
|
||||
|
||||
export class NoteHelpers {
|
||||
|
|
|
@ -306,6 +306,7 @@ export class UserHelpers {
|
|||
}
|
||||
|
||||
public static async getUserFromAcct(acct: string): Promise<User> {
|
||||
if (acct.startsWith("@")) acct = acct.slice(1);
|
||||
const split = acct.toLowerCase().split("@");
|
||||
if (split.length > 2) throw new Error("Invalid acct");
|
||||
return split[1] == null
|
||||
|
|
|
@ -15,7 +15,7 @@ import { convertToWebp } from "@/services/drive/image-processor.js";
|
|||
import { GenerateVideoThumbnail } from "@/services/drive/generate-video-thumbnail.js";
|
||||
import { StatusError } from "@/misc/fetch.js";
|
||||
import { ByteRangeReadable } from "./byte-range-readable.js";
|
||||
import { FILE_TYPE_BROWSERSAFE } from "backend-rs";
|
||||
import { FILE_TYPE_BROWSERSAFE } from "@/const.js";
|
||||
import { inspect } from "node:util";
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
|
|
|
@ -30,6 +30,7 @@ import { koaBody } from "koa-body";
|
|||
import removeTrailingSlash from "koa-remove-trailing-slashes";
|
||||
import { setupEndpointsAuthRoot } from "@/server/api/mastodon/endpoints/auth.js";
|
||||
import { CatchErrorsMiddleware } from "@/server/api/mastodon/middleware/catch-errors.js";
|
||||
import { inspect } from "node:util";
|
||||
|
||||
export const serverLogger = new Logger("server", "gray", false);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { createTemp } from "@/misc/create-temp.js";
|
|||
import { downloadUrl } from "@/misc/download-url.js";
|
||||
import { detectType } from "@/misc/get-file-info.js";
|
||||
import { StatusError } from "@/misc/fetch.js";
|
||||
import { FILE_TYPE_BROWSERSAFE } from "backend-rs";
|
||||
import { FILE_TYPE_BROWSERSAFE } from "@/const.js";
|
||||
import { serverLogger } from "../index.js";
|
||||
import { isMimeImage } from "@/misc/is-mime-image.js";
|
||||
import { inspect } from "node:util";
|
||||
|
|
|
@ -16,13 +16,12 @@ import { KoaAdapter } from "@bull-board/koa";
|
|||
|
||||
import { In, IsNull } from "typeorm";
|
||||
import {
|
||||
MINUTE,
|
||||
DAY,
|
||||
getNoteSummary,
|
||||
stringToAcct,
|
||||
fetchMeta,
|
||||
metaToPugArgs,
|
||||
} from "backend-rs";
|
||||
import { MINUTE, DAY } from "@/const.js";
|
||||
import { config } from "@/config.js";
|
||||
import {
|
||||
Users,
|
||||
|
|
|
@ -7,11 +7,11 @@ import sharp from "sharp";
|
|||
import { IsNull } from "typeorm";
|
||||
import { publishMainStream } from "@/services/stream.js";
|
||||
import {
|
||||
FILE_TYPE_BROWSERSAFE,
|
||||
fetchMeta,
|
||||
genId,
|
||||
publishToDriveFileStream,
|
||||
} from "backend-rs";
|
||||
import { FILE_TYPE_BROWSERSAFE } from "@/const.js";
|
||||
import { contentDisposition } from "@/misc/content-disposition.js";
|
||||
import { getFileInfo } from "@/misc/get-file-info.js";
|
||||
import {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue