From 27c46d7df9d78767765ffb6085641d49285976fd Mon Sep 17 00:00:00 2001 From: Lhcfl Date: Thu, 25 Apr 2024 15:53:29 +0800 Subject: [PATCH 01/41] feat: For notification posts, set a smaller folding threshold --- packages/client/src/components/MkNote.vue | 2 ++ .../src/components/MkNotificationFolded.vue | 11 +++++++ .../src/components/MkSubNoteContent.vue | 32 ++++++++++++------- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/packages/client/src/components/MkNote.vue b/packages/client/src/components/MkNote.vue index 33c70e777e..7b71d4903d 100644 --- a/packages/client/src/components/MkNote.vue +++ b/packages/client/src/components/MkNote.vue @@ -113,6 +113,7 @@ :detailed="true" :detailed-view="detailedView" :parent-id="appearNote.id" + :is-long-judger="isLongJudger" @push="(e) => router.push(notePage(e))" @focusfooter="footerEl!.focus()" @expanded="(e) => setPostExpanded(e)" @@ -325,6 +326,7 @@ const props = defineProps<{ collapsedReply?: boolean; hideFooter?: boolean; hideEmojiViewer?: boolean; + isLongJudger?: (note: entities.Note) => boolean; }>(); const inChannel = inject("inChannel", null); diff --git a/packages/client/src/components/MkNotificationFolded.vue b/packages/client/src/components/MkNotificationFolded.vue index e77467e9a2..f3b9769e84 100644 --- a/packages/client/src/components/MkNotificationFolded.vue +++ b/packages/client/src/components/MkNotificationFolded.vue @@ -60,12 +60,14 @@ class="content" :note="removeReplyTo(notification.note.renote)" :hide-emoji-viewer="true" + :is-long-judger="isLongJudger" /> @@ -120,6 +122,15 @@ const userleft = ref(props.notification.users.length - users.value.length); let readObserver: IntersectionObserver | undefined; let connection: Connection | null = null; +function isLongJudger(note: entities.Note) { + return ( + note.text != null && + (note.text.split("\n").length > 5 || + note.text.length > 300 || + note.files.length > 4) + ); +} + function getText() { let res = ""; switch (props.notification.type) { diff --git a/packages/client/src/components/MkSubNoteContent.vue b/packages/client/src/components/MkSubNoteContent.vue index a0664c28e9..3fb1c2db6b 100644 --- a/packages/client/src/components/MkSubNoteContent.vue +++ b/packages/client/src/components/MkSubNoteContent.vue @@ -196,13 +196,26 @@ import { i18n } from "@/i18n"; import { defaultStore } from "@/store"; import icon from "@/scripts/icon"; -const props = defineProps<{ - note: entities.Note; - parentId?: string; - conversation?: entities.Note[]; - detailed?: boolean; - detailedView?: boolean; -}>(); +const props = withDefaults( + defineProps<{ + note: entities.Note; + parentId?: string; + conversation?: entities.Note[]; + detailed?: boolean; + detailedView?: boolean; + isLongJudger?: (note: entities.Note) => boolean; + }>(), + { + isLongJudger: (note: entities.Note) => { + return ( + note.text != null && + (note.text.split("\n").length > 10 || + note.text.length > 800 || + note.files.length > 4) + ); + }, + }, +); const emit = defineEmits<{ (ev: "push", v): void; @@ -216,10 +229,7 @@ const showMoreButton = ref(); const isLong = !props.detailedView && props.note.cw == null && - ((props.note.text != null && - (props.note.text.split("\n").length > 10 || - props.note.text.length > 800)) || - props.note.files.length > 4); + props.isLongJudger(props.note); const collapsed = ref(props.note.cw == null && isLong); const urls = props.note.text ? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5) From b12d7e4c63ffc920c204af2df3f20f19a9a72880 Mon Sep 17 00:00:00 2001 From: naskya Date: Thu, 25 Apr 2024 19:06:16 +0900 Subject: [PATCH 02/41] refactor (backend): port emoji-meta to backend-rs --- Cargo.lock | 1095 ++++++++++++++++- Cargo.toml | 3 + packages/backend-rs/Cargo.toml | 3 + packages/backend-rs/index.d.ts | 5 + packages/backend-rs/index.js | 3 +- .../backend-rs/src/misc/get_image_size.rs | 206 ++++ packages/backend-rs/src/misc/mod.rs | 1 + packages/backend-rs/src/misc/redis_cache.rs | 20 +- packages/backend/src/misc/emoji-meta.ts | 59 - .../processors/system/local-emoji-size.ts | 4 +- .../src/remote/activitypub/models/note.ts | 11 +- .../server/api/endpoints/admin/emoji/add.ts | 5 +- .../server/api/endpoints/admin/emoji/copy.ts | 5 +- 13 files changed, 1333 insertions(+), 87 deletions(-) create mode 100644 packages/backend-rs/src/misc/get_image_size.rs delete mode 100644 packages/backend/src/misc/emoji-meta.ts diff --git a/Cargo.lock b/Cargo.lock index 5d3dba3b9d..1e5df0278a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "allocator-api2" version = "0.2.18" @@ -77,6 +83,29 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "argon2" version = "0.5.3" @@ -143,6 +172,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec", +] + [[package]] name = "backend-rs" version = "0.0.0" @@ -154,15 +206,18 @@ dependencies = [ "cuid2", "emojis", "idna", + "image", "macro_rs", "napi", "napi-build", "napi-derive", + "nom-exif", "once_cell", "pretty_assertions", "rand", "redis", "regex", + "reqwest", "rmp-serde", "sea-orm", "serde", @@ -240,6 +295,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -255,6 +316,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bitstream-io" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c9989a51171e2e81038ab168b6ae22886fe9ded214430dbb4f41c28cf176da" + [[package]] name = "bitvec" version = "1.0.1" @@ -319,6 +386,12 @@ dependencies = [ "syn_derive", ] +[[package]] +name = "built" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41bfbdb21256b87a8b5e80fab81a8eed158178e812fd7ba451907518b2742f16" + [[package]] name = "bumpalo" version = "3.16.0" @@ -347,12 +420,24 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" + [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.6.0" @@ -364,6 +449,20 @@ name = "cc" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] [[package]] name = "cfg-if" @@ -402,6 +501,12 @@ dependencies = [ "inout", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "combine" version = "4.6.7" @@ -427,6 +532,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -457,6 +572,34 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.11" @@ -472,6 +615,12 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -584,6 +733,15 @@ dependencies = [ "phf", ] +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -617,18 +775,53 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fastrand" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + [[package]] name = "finl_unicode" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flume" version = "0.11.0" @@ -640,6 +833,27 @@ dependencies = [ "spin 0.9.8", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -763,12 +977,51 @@ dependencies = [ "wasi", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "h2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -806,6 +1059,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -845,6 +1104,102 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "hyper" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -878,6 +1233,45 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "image" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d730b085583c4d789dfd07fdcf185be59501666a90c97c40162b37e4fdad272d" +dependencies = [ + "byteorder-lite", + "thiserror", +] + +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + [[package]] name = "indexmap" version = "2.2.6" @@ -908,6 +1302,23 @@ dependencies = [ "generic-array", ] +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itertools" version = "0.12.1" @@ -923,6 +1334,21 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.69" @@ -950,12 +1376,29 @@ dependencies = [ "spin 0.5.2", ] +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + [[package]] name = "libloading" version = "0.8.3" @@ -1005,6 +1448,15 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "macro_rs" version = "0.0.0" @@ -1017,6 +1469,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "md-5" version = "0.10.6" @@ -1033,6 +1495,12 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1046,6 +1514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", + "simd-adler32", ] [[package]] @@ -1120,6 +1589,30 @@ dependencies = [ "libloading", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nom" version = "7.1.3" @@ -1130,6 +1623,24 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom-exif" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da85c96824dc1b583f55fd2c39e53b04482b4c199f25db27868db334bf06407" +dependencies = [ + "chrono", + "nom", + "regex", + "thiserror", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1197,6 +1708,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -1264,6 +1786,50 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "ordered-float" version = "3.9.2" @@ -1290,7 +1856,7 @@ version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro-error", "proc-macro2", "quote", @@ -1376,6 +1942,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -1415,6 +2001,19 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1443,7 +2042,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit", + "toml_edit 0.21.1", ] [[package]] @@ -1479,6 +2078,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote", + "syn 2.0.58", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -1499,6 +2117,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.36" @@ -1544,6 +2177,76 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc13288f5ab39e6d7c9d501759712e6969fcc9734220846fc9ed26cae2cc4234" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redis" version = "0.25.3" @@ -1606,6 +2309,58 @@ dependencies = [ "bytecheck", ] +[[package]] +name = "reqwest" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +dependencies = [ + "base64 0.22.0", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.1.2", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rgb" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.17.8" @@ -1747,6 +2502,22 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.0", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -1769,6 +2540,15 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1791,7 +2571,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro-error", "proc-macro2", "quote", @@ -1832,7 +2612,7 @@ version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e115c6b078e013aa963cc2d38c196c2c40b05f03d0ac872fe06b6e0d5265603" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "sea-bae", @@ -1879,6 +2659,29 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "security-framework" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.22" @@ -1916,6 +2719,27 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -1995,6 +2819,21 @@ dependencies = [ "rand_core", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "simdutf8" version = "0.1.4" @@ -2112,7 +2951,7 @@ dependencies = [ "percent-encoding", "rust_decimal", "rustls", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "serde", "serde_json", "sha2", @@ -2149,7 +2988,7 @@ checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" dependencies = [ "dotenvy", "either", - "heck", + "heck 0.4.1", "hex", "once_cell", "proc-macro2", @@ -2322,7 +3161,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -2369,12 +3208,58 @@ dependencies = [ "syn 2.0.58", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + [[package]] name = "tempfile" version = "3.10.1" @@ -2417,6 +3302,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" version = "0.3.36" @@ -2493,6 +3389,16 @@ dependencies = [ "syn 2.0.58", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.15" @@ -2504,11 +3410,40 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.12", +] + [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -2518,9 +3453,50 @@ checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap", "toml_datetime", - "winnow", + "winnow 0.5.40", ] +[[package]] +name = "toml_edit" +version = "0.22.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.6", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" @@ -2579,6 +3555,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.17.0" @@ -2656,6 +3638,17 @@ dependencies = [ "serde", ] +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.0" @@ -2668,12 +3661,27 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2711,6 +3719,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.92" @@ -2740,12 +3760,28 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "whoami" version = "1.5.1" @@ -2935,6 +3971,25 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wyz" version = "0.5.1" @@ -2975,3 +4030,27 @@ name = "zeroize" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index 056f1006b8..cda98ba300 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,8 @@ convert_case = "0.6.0" cuid2 = "0.1.2" emojis = "0.6.1" idna = "0.5.0" +image = "0.25.1" +nom-exif = "1.2.0" once_cell = "1.19.0" pretty_assertions = "1.4.0" proc-macro2 = "1.0.79" @@ -24,6 +26,7 @@ quote = "1.0.36" rand = "0.8.5" redis = "0.25.3" regex = "1.10.4" +reqwest = "0.12.4" rmp-serde = "1.2.0" sea-orm = "0.12.15" serde = "1.0.197" diff --git a/packages/backend-rs/Cargo.toml b/packages/backend-rs/Cargo.toml index 22c786c6d5..8117c14a58 100644 --- a/packages/backend-rs/Cargo.toml +++ b/packages/backend-rs/Cargo.toml @@ -24,10 +24,13 @@ chrono = { workspace = true } cuid2 = { workspace = true } emojis = { workspace = true } idna = { workspace = true } +image = { workspace = true } +nom-exif = { workspace = true } once_cell = { workspace = true } rand = { workspace = true } redis = { workspace = true } regex = { workspace = true } +reqwest = { workspace = true, features = ["blocking"] } rmp-serde = { workspace = true } sea-orm = { workspace = true, features = ["sqlx-postgres", "runtime-tokio-rustls"] } serde = { workspace = true, features = ["derive"] } diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts index c0f0406e74..f2cecae0fa 100644 --- a/packages/backend-rs/index.d.ts +++ b/packages/backend-rs/index.d.ts @@ -248,6 +248,11 @@ export function sqlLikeEscape(src: string): string export function safeForSql(src: string): boolean /** Convert milliseconds to a human readable string */ export function formatMilliseconds(milliseconds: number): string +export interface ImageSize { + width: number + height: number +} +export function getImageSizeFromUrl(url: string): Promise /** TODO: handle name collisions better */ export interface NoteLikeForGetNoteSummary { fileIds: Array diff --git a/packages/backend-rs/index.js b/packages/backend-rs/index.js index 1c3a2f4195..aa3776645c 100644 --- a/packages/backend-rs/index.js +++ b/packages/backend-rs/index.js @@ -310,7 +310,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, ChatEvent, publishToChatStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding +const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, ChatEvent, publishToChatStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding module.exports.SECOND = SECOND module.exports.MINUTE = MINUTE @@ -337,6 +337,7 @@ module.exports.isUnicodeEmoji = isUnicodeEmoji module.exports.sqlLikeEscape = sqlLikeEscape module.exports.safeForSql = safeForSql module.exports.formatMilliseconds = formatMilliseconds +module.exports.getImageSizeFromUrl = getImageSizeFromUrl module.exports.getNoteSummary = getNoteSummary module.exports.toMastodonId = toMastodonId module.exports.fromMastodonId = fromMastodonId diff --git a/packages/backend-rs/src/misc/get_image_size.rs b/packages/backend-rs/src/misc/get_image_size.rs new file mode 100644 index 0000000000..788cd6f571 --- /dev/null +++ b/packages/backend-rs/src/misc/get_image_size.rs @@ -0,0 +1,206 @@ +use crate::misc::redis_cache::{get_cache, set_cache, CacheError}; +use image::{io::Reader, ImageError, ImageFormat}; +use nom_exif::{parse_jpeg_exif, EntryValue, ExifTag}; +use once_cell::sync::OnceCell; +use std::io::Cursor; +use std::time::Duration; +use tokio::sync::Mutex; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Redis cache error: {0}")] + CacheErr(#[from] CacheError), + #[error("Reqewst error: {0}")] + ReqewstErr(#[from] reqwest::Error), + #[error("Image decoding error: {0}")] + ImageErr(#[from] ImageError), + #[error("Image decoding error: {0}")] + IoErr(#[from] std::io::Error), + #[error("Exif extraction error: {0}")] + ExifErr(#[from] nom_exif::Error), + #[error("Emoji meta attempt limit exceeded: {0}")] + TooManyAttempts(String), + #[error("Unsupported image type: {0}")] + UnsupportedImageErr(String), +} + +const BROWSER_SAFE_IMAGE_TYPES: [ImageFormat; 8] = [ + ImageFormat::Png, + ImageFormat::Jpeg, + ImageFormat::Gif, + ImageFormat::WebP, + ImageFormat::Tiff, + ImageFormat::Bmp, + ImageFormat::Ico, + ImageFormat::Avif, +]; + +static CLIENT: OnceCell = OnceCell::new(); + +fn client() -> Result { + CLIENT + .get_or_try_init(|| { + reqwest::Client::builder() + .timeout(Duration::from_secs(5)) + .build() + }) + .cloned() +} + +static MTX_GUARD: Mutex<()> = Mutex::const_new(()); + +#[derive(Debug, PartialEq)] +#[crate::export(object)] +pub struct ImageSize { + pub width: u32, + pub height: u32, +} + +#[crate::export] +pub async fn get_image_size_from_url(url: &str) -> Result { + let attempted: bool; + + { + let _ = MTX_GUARD.lock().await; + + let key = format!("fetchImage:{}", url); + attempted = get_cache::(&key)?.is_some(); + + if !attempted { + set_cache(&key, &true, 10 * 60)?; + } + } + + if attempted { + tracing::warn!("attempt limit exceeded: {}", url); + return Err(Error::TooManyAttempts(url.to_string())); + } + + tracing::info!("retrieving image size from {}", url); + + let image_bytes = client()?.get(url).send().await?.bytes().await?; + let reader = Reader::new(Cursor::new(&image_bytes)).with_guessed_format()?; + + let format = reader.format(); + if format.is_none() || !BROWSER_SAFE_IMAGE_TYPES.contains(&format.unwrap()) { + return Err(Error::UnsupportedImageErr(format!("{:?}", format))); + } + + let size = reader.into_dimensions()?; + + let res = ImageSize { + width: size.0, + height: size.1, + }; + + if format.unwrap() != ImageFormat::Jpeg { + return Ok(res); + } + + // handle jpeg orientation + // https://magnushoff.com/articles/jpeg-orientation/ + + let exif = parse_jpeg_exif(&*image_bytes)?; + if exif.is_none() { + return Ok(res); + } + + let orientation = exif.unwrap().get_value(&ExifTag::Orientation)?; + let rotated = + orientation.is_some() && matches!(orientation.unwrap(), EntryValue::U32(v) if v >= 5); + + if !rotated { + return Ok(res); + } + + Ok(ImageSize { + width: size.1, + height: size.0, + }) +} + +#[cfg(test)] +mod unit_test { + use super::{get_image_size_from_url, ImageSize}; + use crate::misc::redis_cache::delete_cache; + use pretty_assertions::assert_eq; + + #[tokio::test] + async fn test_get_image_size() { + let png_url_1 = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/splash.png"; + let png_url_2 = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/notification-badges/at.png"; + let png_url_3 = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/api-doc.png"; + let rotated_jpeg_url = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/test/resources/rotate.jpg"; + let webp_url_1 = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/custom/assets/badges/error.webp"; + let webp_url_2 = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/screenshots/1.webp"; + let ico_url = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/favicon.ico"; + let mp3_url = "https://firefish.dev/firefish/firefish/-/blob/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/sounds/aisha/1.mp3"; + + // Delete caches in case you run this test multiple times + // (should be disabled in CI tasks) + delete_cache(&format!("fetchImage:{}", png_url_1)).unwrap(); + delete_cache(&format!("fetchImage:{}", png_url_2)).unwrap(); + delete_cache(&format!("fetchImage:{}", png_url_3)).unwrap(); + delete_cache(&format!("fetchImage:{}", rotated_jpeg_url)).unwrap(); + delete_cache(&format!("fetchImage:{}", webp_url_1)).unwrap(); + delete_cache(&format!("fetchImage:{}", webp_url_2)).unwrap(); + delete_cache(&format!("fetchImage:{}", ico_url)).unwrap(); + delete_cache(&format!("fetchImage:{}", mp3_url)).unwrap(); + + let png_size_1 = ImageSize { + width: 1024, + height: 1024, + }; + let png_size_2 = ImageSize { + width: 96, + height: 96, + }; + let png_size_3 = ImageSize { + width: 1024, + height: 354, + }; + let rotated_jpeg_size = ImageSize { + width: 256, + height: 512, + }; + let webp_size_1 = ImageSize { + width: 256, + height: 256, + }; + let webp_size_2 = ImageSize { + width: 1080, + height: 2340, + }; + let ico_size = ImageSize { + width: 256, + height: 256, + }; + + assert_eq!( + png_size_1, + get_image_size_from_url(png_url_1).await.unwrap() + ); + assert_eq!( + png_size_2, + get_image_size_from_url(png_url_2).await.unwrap() + ); + assert_eq!( + png_size_3, + get_image_size_from_url(png_url_3).await.unwrap() + ); + assert_eq!( + rotated_jpeg_size, + get_image_size_from_url(rotated_jpeg_url).await.unwrap() + ); + assert_eq!( + webp_size_1, + get_image_size_from_url(webp_url_1).await.unwrap() + ); + assert_eq!( + webp_size_2, + get_image_size_from_url(webp_url_2).await.unwrap() + ); + assert_eq!(ico_size, get_image_size_from_url(ico_url).await.unwrap()); + assert!(get_image_size_from_url(mp3_url).await.is_err()); + } +} diff --git a/packages/backend-rs/src/misc/mod.rs b/packages/backend-rs/src/misc/mod.rs index 049c744f62..0ba15dc8e4 100644 --- a/packages/backend-rs/src/misc/mod.rs +++ b/packages/backend-rs/src/misc/mod.rs @@ -6,6 +6,7 @@ pub mod convert_host; pub mod emoji; pub mod escape_sql; pub mod format_milliseconds; +pub mod get_image_size; pub mod get_note_summary; pub mod mastodon_id; pub mod meta; diff --git a/packages/backend-rs/src/misc/redis_cache.rs b/packages/backend-rs/src/misc/redis_cache.rs index d4924bb646..81ac840b8d 100644 --- a/packages/backend-rs/src/misc/redis_cache.rs +++ b/packages/backend-rs/src/misc/redis_cache.rs @@ -3,7 +3,7 @@ use redis::{Commands, RedisError}; use serde::{Deserialize, Serialize}; #[derive(thiserror::Error, Debug)] -pub enum Error { +pub enum CacheError { #[error("Redis error: {0}")] RedisError(#[from] RedisError), #[error("Data serialization error: {0}")] @@ -12,27 +12,37 @@ pub enum Error { DeserializeError(#[from] rmp_serde::decode::Error), } +fn prefix_key(key: &str) -> String { + redis_key(format!("cache:{}", key)) +} + pub fn set_cache Deserialize<'a> + Serialize>( key: &str, value: &V, expire_seconds: u64, -) -> Result<(), Error> { +) -> Result<(), CacheError> { redis_conn()?.set_ex( - redis_key(key), + prefix_key(key), rmp_serde::encode::to_vec(&value)?, expire_seconds, )?; Ok(()) } -pub fn get_cache Deserialize<'a> + Serialize>(key: &str) -> Result, Error> { - let serialized_value: Option> = redis_conn()?.get(redis_key(key))?; +pub fn get_cache Deserialize<'a> + Serialize>( + key: &str, +) -> Result, CacheError> { + let serialized_value: Option> = redis_conn()?.get(prefix_key(key))?; Ok(match serialized_value { Some(v) => Some(rmp_serde::from_slice::(v.as_ref())?), None => None, }) } +pub fn delete_cache(key: &str) -> Result<(), CacheError> { + Ok(redis_conn()?.del(prefix_key(key))?) +} + #[cfg(test)] mod unit_test { use super::{get_cache, set_cache}; diff --git a/packages/backend/src/misc/emoji-meta.ts b/packages/backend/src/misc/emoji-meta.ts deleted file mode 100644 index f970658f8e..0000000000 --- a/packages/backend/src/misc/emoji-meta.ts +++ /dev/null @@ -1,59 +0,0 @@ -import probeImageSize from "probe-image-size"; -import { Mutex } from "redis-semaphore"; - -import { FILE_TYPE_BROWSERSAFE } from "backend-rs"; -import Logger from "@/services/logger.js"; -import { Cache } from "./cache.js"; -import { redisClient } from "@/db/redis.js"; -import { inspect } from "node:util"; - -export type Size = { - width: number; - height: number; -}; - -const cache = new Cache("emojiMeta", 60 * 10); // once every 10 minutes for the same url -const logger = new Logger("emoji"); - -export async function getEmojiSize(url: string): Promise { - let attempted = true; - - const lock = new Mutex(redisClient, "getEmojiSize"); - await lock.acquire(); - - try { - attempted = (await cache.get(url)) === true; - if (!attempted) { - await cache.set(url, true); - } - } finally { - await lock.release(); - } - - if (attempted) { - logger.warn(`Attempt limit exceeded: ${url}`); - throw new Error("Too many attempts"); - } - - try { - logger.debug(`Retrieving emoji size from ${url}`); - const { width, height, mime } = await probeImageSize(url, { - timeout: 5000, - }); - if (!(mime.startsWith("image/") && FILE_TYPE_BROWSERSAFE.includes(mime))) { - throw new Error("Unsupported image type"); - } - return { width, height }; - } catch (e) { - throw new Error(`Unable to retrieve metadata:\n${inspect(e)}`); - } -} - -export function getNormalSize( - { width, height }: Size, - orientation?: number, -): Size { - return (orientation || 0) >= 5 - ? { width: height, height: width } - : { width, height }; -} diff --git a/packages/backend/src/queue/processors/system/local-emoji-size.ts b/packages/backend/src/queue/processors/system/local-emoji-size.ts index bd4dc6b5d4..fbd036ee57 100644 --- a/packages/backend/src/queue/processors/system/local-emoji-size.ts +++ b/packages/backend/src/queue/processors/system/local-emoji-size.ts @@ -3,7 +3,7 @@ import { IsNull } from "typeorm"; import { Emojis } from "@/models/index.js"; import { queueLogger } from "../../logger.js"; -import { getEmojiSize } from "@/misc/emoji-meta.js"; +import { getImageSizeFromUrl } from "backend-rs"; import { inspect } from "node:util"; const logger = queueLogger.createSubLogger("local-emoji-size"); @@ -21,7 +21,7 @@ export async function setLocalEmojiSizes( for (let i = 0; i < emojis.length; i++) { try { - const size = await getEmojiSize(emojis[i].publicUrl); + const size = await getImageSizeFromUrl(emojis[i].publicUrl); await Emojis.update(emojis[i].id, { width: size.width || null, height: size.height || null, diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index a7be3e9846..7b79bd23ed 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -13,7 +13,7 @@ import { extractPollFromQuestion } from "./question.js"; import vote from "@/services/note/polls/vote.js"; import { apLogger } from "../logger.js"; import type { DriveFile } from "@/models/entities/drive-file.js"; -import { extractHost, isSameOrigin, toPuny } from "backend-rs"; +import { type ImageSize, extractHost, getImageSizeFromUrl, isSameOrigin, toPuny } from "backend-rs"; import { Emojis, Polls, @@ -46,7 +46,6 @@ import { UserProfiles } from "@/models/index.js"; import { In } from "typeorm"; import { config } from "@/config.js"; import { truncate } from "@/misc/truncate.js"; -import { type Size, getEmojiSize } from "@/misc/emoji-meta.js"; import { langmap } from "@/misc/langmap.js"; import { inspect } from "node:util"; @@ -488,9 +487,9 @@ export async function extractEmojis( tag.icon!.url !== exists.originalUrl || !(exists.width && exists.height) ) { - let size: Size = { width: 0, height: 0 }; + let size: ImageSize = { width: 0, height: 0 }; try { - size = await getEmojiSize(tag.icon!.url); + size = await getImageSizeFromUrl(tag.icon!.url); } catch { /* skip if any error happens */ } @@ -520,9 +519,9 @@ export async function extractEmojis( apLogger.info(`register emoji host=${host}, name=${name}`); - let size: Size = { width: 0, height: 0 }; + let size: ImageSize = { width: 0, height: 0 }; try { - size = await getEmojiSize(tag.icon!.url); + size = await getImageSizeFromUrl(tag.icon!.url); } catch { /* skip if any error happens */ } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 8fa7579ceb..0224e19b04 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -1,12 +1,11 @@ import define from "@/server/api/define.js"; import { Emojis, DriveFiles } from "@/models/index.js"; -import { genId } from "backend-rs"; +import { genId, getImageSizeFromUrl } from "backend-rs"; import { insertModerationLog } from "@/services/insert-moderation-log.js"; import { ApiError } from "@/server/api/error.js"; import rndstr from "rndstr"; import { publishBroadcastStream } from "@/services/stream.js"; import { db } from "@/db/postgre.js"; -import { getEmojiSize } from "@/misc/emoji-meta.js"; export const meta = { tags: ["admin", "emoji"], @@ -49,7 +48,7 @@ export default define(meta, paramDef, async (ps, me) => { ? file.name.split(".")[0] : `_${rndstr("a-z0-9", 8)}_`; - const size = await getEmojiSize(file.url); + const size = await getImageSizeFromUrl(file.url); const emoji = await Emojis.insert({ id: genId(), diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 424baf4409..b5f67becdf 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -1,12 +1,11 @@ import define from "@/server/api/define.js"; import { Emojis } from "@/models/index.js"; -import { genId } from "backend-rs"; +import { genId, getImageSizeFromUrl } from "backend-rs"; import { ApiError } from "@/server/api/error.js"; import type { DriveFile } from "@/models/entities/drive-file.js"; import { uploadFromUrl } from "@/services/drive/upload-from-url.js"; import { publishBroadcastStream } from "@/services/stream.js"; import { db } from "@/db/postgre.js"; -import { getEmojiSize } from "@/misc/emoji-meta.js"; export const meta = { tags: ["admin", "emoji"], @@ -76,7 +75,7 @@ export default define(meta, paramDef, async (ps, me) => { throw new ApiError(); } - const size = await getEmojiSize(driveFile.url); + const size = await getImageSizeFromUrl(driveFile.url); const copied = await Emojis.insert({ id: genId(), From 214e999c8c9b14dc09661efbed60085b29d90c4c Mon Sep 17 00:00:00 2001 From: naskya Date: Thu, 25 Apr 2024 19:14:16 +0900 Subject: [PATCH 03/41] test (backend-rs): add test against animated gif --- packages/backend-rs/src/misc/get_image_size.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/backend-rs/src/misc/get_image_size.rs b/packages/backend-rs/src/misc/get_image_size.rs index 788cd6f571..2157d7cf3d 100644 --- a/packages/backend-rs/src/misc/get_image_size.rs +++ b/packages/backend-rs/src/misc/get_image_size.rs @@ -134,6 +134,7 @@ mod unit_test { let webp_url_1 = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/custom/assets/badges/error.webp"; let webp_url_2 = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/screenshots/1.webp"; let ico_url = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/favicon.ico"; + let gif_url = "https://firefish.dev/firefish/firefish/-/raw/b9c3dfbd3d473cb2cee20c467eeae780bc401271/packages/backend/test/resources/anime.gif"; let mp3_url = "https://firefish.dev/firefish/firefish/-/blob/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/sounds/aisha/1.mp3"; // Delete caches in case you run this test multiple times @@ -145,6 +146,7 @@ mod unit_test { delete_cache(&format!("fetchImage:{}", webp_url_1)).unwrap(); delete_cache(&format!("fetchImage:{}", webp_url_2)).unwrap(); delete_cache(&format!("fetchImage:{}", ico_url)).unwrap(); + delete_cache(&format!("fetchImage:{}", gif_url)).unwrap(); delete_cache(&format!("fetchImage:{}", mp3_url)).unwrap(); let png_size_1 = ImageSize { @@ -175,6 +177,10 @@ mod unit_test { width: 256, height: 256, }; + let gif_size = ImageSize { + width: 256, + height: 256, + }; assert_eq!( png_size_1, @@ -201,6 +207,7 @@ mod unit_test { get_image_size_from_url(webp_url_2).await.unwrap() ); assert_eq!(ico_size, get_image_size_from_url(ico_url).await.unwrap()); + assert_eq!(gif_size, get_image_size_from_url(gif_url).await.unwrap()); assert!(get_image_size_from_url(mp3_url).await.is_err()); } } From 0b7385e16acf11dc59b975e9705af87b554025c5 Mon Sep 17 00:00:00 2001 From: naskya Date: Thu, 25 Apr 2024 19:15:12 +0900 Subject: [PATCH 04/41] chore: format --- packages/backend/src/remote/activitypub/models/note.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index 7b79bd23ed..7f0c8c820d 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -13,7 +13,13 @@ import { extractPollFromQuestion } from "./question.js"; import vote from "@/services/note/polls/vote.js"; import { apLogger } from "../logger.js"; import type { DriveFile } from "@/models/entities/drive-file.js"; -import { type ImageSize, extractHost, getImageSizeFromUrl, isSameOrigin, toPuny } from "backend-rs"; +import { + type ImageSize, + extractHost, + getImageSizeFromUrl, + isSameOrigin, + toPuny, +} from "backend-rs"; import { Emojis, Polls, From 5de6e07dfa7cdcb41b6e57bea82b4538e2167cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=81=E5=91=A8=E9=83=A8=E8=90=BD?= Date: Thu, 25 Apr 2024 20:34:31 +0800 Subject: [PATCH 05/41] chore: remove unused id config --- .config/devenv.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.config/devenv.yml b/.config/devenv.yml index 5e3ca24e8d..3c980914a9 100644 --- a/.config/devenv.yml +++ b/.config/devenv.yml @@ -13,8 +13,6 @@ redis: host: firefish_redis port: 6379 -id: 'aid' - #allowedPrivateNetworks: [ # '10.69.1.0/24' #] From bb984a860856b9f4eb3998c838a6147f2d03813e Mon Sep 17 00:00:00 2001 From: Sal Rahman Date: Mon, 21 Aug 2023 21:41:40 -0700 Subject: [PATCH 06/41] Some work on the popup Some more work on the modal Some more work done Some initial work on adding the QR code Some more changes Done --- locales/en-US.yml | 1 + locales/fr-FR.yml | 2 + packages/client/package.json | 5 ++ packages/client/src/components/FfQrCode.vue | 70 ++++++++++++++++++++ packages/client/src/os.ts | 14 ++++ packages/client/src/scripts/get-user-menu.ts | 7 ++ 6 files changed, 99 insertions(+) create mode 100644 packages/client/src/components/FfQrCode.vue diff --git a/locales/en-US.yml b/locales/en-US.yml index 7ccda1426c..cb96caa823 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1157,6 +1157,7 @@ addRe: "Add \"re:\" at the beginning of comment in reply to a post with a conten confirm: "Confirm" importZip: "Import ZIP" exportZip: "Export ZIP" +getQrCode: "Get QR code" emojiPackCreator: "Emoji pack creator" indexable: "Indexable" indexableDescription: "Allow built-in search to show your public posts" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 6367dda9f4..5d7a1ecef8 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -928,6 +928,8 @@ colored: "Coloré" label: "Étiquette" localOnly: "Local seulement" account: "Comptes" +getQrCode: "Obtenir le code QR" + _emailUnavailable: used: "Adresse non disponible" format: "Le format de cette adresse de courriel est invalide" diff --git a/packages/client/package.json b/packages/client/package.json index 97a5f83ef7..8b9184ad38 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -28,6 +28,7 @@ "@types/matter-js": "0.19.6", "@types/prismjs": "^1.26.3", "@types/punycode": "2.1.4", + "@types/qrcode": "1.5.1", "@types/seedrandom": "3.0.8", "@types/textarea-caret": "^3.0.3", "@types/throttle-debounce": "5.0.2", @@ -60,13 +61,17 @@ "insert-text-at-cursor": "0.3.0", "json5": "2.2.3", "katex": "0.16.10", + "long": "^5.2.3", "libopenmpt-wasm": "github:TheEssem/libopenmpt-packaging#build", "matter-js": "0.19.0", "mfm-js": "0.24.0", + "multer": "1.4.4-lts.1", "moment": "2.30.1", "photoswipe": "5.4.3", "prismjs": "1.29.0", "punycode": "2.3.1", + "qrcode": "1.5.3", + "qrcode-vue3": "^1.6.8", "rollup": "4.14.2", "s-age": "1.1.2", "sass": "1.75.0", diff --git a/packages/client/src/components/FfQrCode.vue b/packages/client/src/components/FfQrCode.vue new file mode 100644 index 0000000000..9dca107406 --- /dev/null +++ b/packages/client/src/components/FfQrCode.vue @@ -0,0 +1,70 @@ + + + + + diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts index c7c62852de..61629863ba 100644 --- a/packages/client/src/os.ts +++ b/packages/client/src/os.ts @@ -7,6 +7,7 @@ import type { Component, MaybeRef, Ref } from "vue"; import { defineAsyncComponent, markRaw, ref } from "vue"; import { i18n } from "./i18n"; import MkDialog from "@/components/MkDialog.vue"; +import FfQrCode from "@/components/FfQrCode.vue"; import MkPostFormDialog from "@/components/MkPostFormDialog.vue"; import MkToast from "@/components/MkToast.vue"; import MkWaitingDialog from "@/components/MkWaitingDialog.vue"; @@ -1003,6 +1004,19 @@ export function post( }); } +export async function displayQrCode(qrCode: string) { + (await new Promise<(() => void) | undefined>((resolve) => { + let dispose: (() => void) | undefined; + popup(FfQrCode, { qrCode }, { + closed: () => { + resolve(dispose); + } + }).then((res) => { + dispose = res.dispose + }) + }))?.(); +} + export const deckGlobalEvents = new EventEmitter(); /* diff --git a/packages/client/src/scripts/get-user-menu.ts b/packages/client/src/scripts/get-user-menu.ts index 880ce481c7..713023eada 100644 --- a/packages/client/src/scripts/get-user-menu.ts +++ b/packages/client/src/scripts/get-user-menu.ts @@ -290,6 +290,13 @@ export function getUserMenu(user, router: Router = mainRouter) { os.post({ specified: user }); }, }, + { + icon: "ph-qr-code ph-bold ph-lg", + text: i18n.ts.getQrCode, + action: () => { + os.displayQrCode(window.location.href) + } + }, isSignedIn(me) && me.id !== user.id ? { type: "link", From fefed2d4a4810996af4b5732d6a3f7576cafb398 Mon Sep 17 00:00:00 2001 From: Sal Rahman Date: Mon, 28 Aug 2023 10:03:40 -0700 Subject: [PATCH 07/41] FfQrCode -> MkQrCode --- packages/client/src/components/{FfQrCode.vue => MkQrCode.vue} | 2 -- packages/client/src/os.ts | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) rename packages/client/src/components/{FfQrCode.vue => MkQrCode.vue} (96%) diff --git a/packages/client/src/components/FfQrCode.vue b/packages/client/src/components/MkQrCode.vue similarity index 96% rename from packages/client/src/components/FfQrCode.vue rename to packages/client/src/components/MkQrCode.vue index 9dca107406..c5a389b095 100644 --- a/packages/client/src/components/FfQrCode.vue +++ b/packages/client/src/components/MkQrCode.vue @@ -20,8 +20,6 @@ import MkButton from "@/components/MkButton.vue"; import { i18n } from "@/i18n"; import QRCodeVue3 from "qrcode-vue3"; -// const profileUrl = window.location.href; - const props = defineProps<{ qrCode: string }>(); diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts index 61629863ba..c9b11063ea 100644 --- a/packages/client/src/os.ts +++ b/packages/client/src/os.ts @@ -7,7 +7,7 @@ import type { Component, MaybeRef, Ref } from "vue"; import { defineAsyncComponent, markRaw, ref } from "vue"; import { i18n } from "./i18n"; import MkDialog from "@/components/MkDialog.vue"; -import FfQrCode from "@/components/FfQrCode.vue"; +import MkQrCode from "@/components/MkQrCode.vue"; import MkPostFormDialog from "@/components/MkPostFormDialog.vue"; import MkToast from "@/components/MkToast.vue"; import MkWaitingDialog from "@/components/MkWaitingDialog.vue"; @@ -1007,7 +1007,7 @@ export function post( export async function displayQrCode(qrCode: string) { (await new Promise<(() => void) | undefined>((resolve) => { let dispose: (() => void) | undefined; - popup(FfQrCode, { qrCode }, { + popup(MkQrCode, { qrCode }, { closed: () => { resolve(dispose); } From 6aecf12067758431fd19ed509bb3e897e92c23c4 Mon Sep 17 00:00:00 2001 From: Sal Rahman Date: Tue, 29 Aug 2023 09:38:23 -0700 Subject: [PATCH 08/41] QR code update --- packages/client/src/scripts/get-user-menu.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/scripts/get-user-menu.ts b/packages/client/src/scripts/get-user-menu.ts index 713023eada..ca43e40716 100644 --- a/packages/client/src/scripts/get-user-menu.ts +++ b/packages/client/src/scripts/get-user-menu.ts @@ -294,7 +294,7 @@ export function getUserMenu(user, router: Router = mainRouter) { icon: "ph-qr-code ph-bold ph-lg", text: i18n.ts.getQrCode, action: () => { - os.displayQrCode(window.location.href) + os.displayQrCode(`https://${host}/authorize-follow?acct=${user.username}`) } }, isSignedIn(me) && me.id !== user.id From ab357233db879babf73afcbde272f6475f970b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=81=E5=91=A8=E9=83=A8=E8=90=BD?= Date: Thu, 25 Apr 2024 20:40:18 +0800 Subject: [PATCH 09/41] chore: format --- packages/client/src/components/MkQrCode.vue | 2 +- packages/client/src/os.ts | 24 ++++++++++++-------- packages/client/src/scripts/get-user-menu.ts | 6 +++-- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/client/src/components/MkQrCode.vue b/packages/client/src/components/MkQrCode.vue index c5a389b095..2984f88697 100644 --- a/packages/client/src/components/MkQrCode.vue +++ b/packages/client/src/components/MkQrCode.vue @@ -21,7 +21,7 @@ import { i18n } from "@/i18n"; import QRCodeVue3 from "qrcode-vue3"; const props = defineProps<{ - qrCode: string + qrCode: string; }>(); const modal = shallowRef>(); diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts index c9b11063ea..b53cb3f303 100644 --- a/packages/client/src/os.ts +++ b/packages/client/src/os.ts @@ -1005,16 +1005,22 @@ export function post( } export async function displayQrCode(qrCode: string) { - (await new Promise<(() => void) | undefined>((resolve) => { - let dispose: (() => void) | undefined; - popup(MkQrCode, { qrCode }, { - closed: () => { - resolve(dispose); - } - }).then((res) => { - dispose = res.dispose + ( + await new Promise<(() => void) | undefined>((resolve) => { + let dispose: (() => void) | undefined; + popup( + MkQrCode, + { qrCode }, + { + closed: () => { + resolve(dispose); + }, + }, + ).then((res) => { + dispose = res.dispose; + }); }) - }))?.(); + )?.(); } export const deckGlobalEvents = new EventEmitter(); diff --git a/packages/client/src/scripts/get-user-menu.ts b/packages/client/src/scripts/get-user-menu.ts index ca43e40716..5f6108d008 100644 --- a/packages/client/src/scripts/get-user-menu.ts +++ b/packages/client/src/scripts/get-user-menu.ts @@ -294,8 +294,10 @@ export function getUserMenu(user, router: Router = mainRouter) { icon: "ph-qr-code ph-bold ph-lg", text: i18n.ts.getQrCode, action: () => { - os.displayQrCode(`https://${host}/authorize-follow?acct=${user.username}`) - } + os.displayQrCode( + `https://${host}/authorize-follow?acct=${user.username}`, + ); + }, }, isSignedIn(me) && me.id !== user.id ? { From af109b45ef31783deef68653965bdb4a30e76b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=81=E5=91=A8=E9=83=A8=E8=90=BD?= Date: Thu, 25 Apr 2024 20:40:59 +0800 Subject: [PATCH 10/41] locale: update translations --- locales/zh-CN.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index bfc0915101..a8262ac0ba 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1974,6 +1974,7 @@ origin: 起源 confirm: 确认 importZip: 导入 ZIP exportZip: 导出 ZIP +getQrCode: "获取二维码" emojiPackCreator: 表情包创建工具 objectStorageS3ForcePathStyleDesc: 打开此选项可构建格式为 "s3.amazonaws.com//" 而非 ".s3.amazonaws.com" 的端点 URL。 From 22b52ac3d3f2f1ae8f142f4c721a5becf6359d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=81=E5=91=A8=E9=83=A8=E8=90=BD?= Date: Thu, 25 Apr 2024 20:45:41 +0800 Subject: [PATCH 11/41] feat: follow-me --- locales/en-US.yml | 4 + locales/zh-CN.yml | 4 + packages/client/src/pages/follow-me.vue | 78 ++++++++++++++++++++ packages/client/src/router.ts | 4 + packages/client/src/scripts/get-user-menu.ts | 20 ++++- 5 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 packages/client/src/pages/follow-me.vue diff --git a/locales/en-US.yml b/locales/en-US.yml index cb96caa823..72d660e273 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1011,6 +1011,8 @@ isSystemAccount: "This account is created and automatically operated by the syst Please do not moderate, edit, delete, or otherwise tamper with this account, or it may break your server." typeToConfirm: "Please enter {x} to confirm" +useThisAccountConfirm: "Do you want to continue with this account?" +inputAccountId: "Please input your account (e.g., @firefish@info.firefish.dev )" deleteAccount: "Delete account" document: "Documentation" numberOfPageCache: "Number of cached pages" @@ -1158,6 +1160,8 @@ confirm: "Confirm" importZip: "Import ZIP" exportZip: "Export ZIP" getQrCode: "Get QR code" +remoteFollow: "Remote Follow" +remoteFollowUrl: "Remote Follow URL" emojiPackCreator: "Emoji pack creator" indexable: "Indexable" indexableDescription: "Allow built-in search to show your public posts" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index a8262ac0ba..984f7e5831 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -879,6 +879,8 @@ driveCapOverrideCaption: "输入 0 或以下的值将容量重置为默认值。 requireAdminForView: "您需要使用管理员账号登录才能查看。" isSystemAccount: "该账号由系统自动创建。请不要修改、编辑、删除或以其它方式篡改这个账号,否则可能会破坏您的服务器。" typeToConfirm: "输入 {x} 以确认操作" +useThisAccountConfirm: "您想使用此帐户继续执行此操作吗?" +inputAccountId: "请输入您的帐户(例如 @firefish@info.firefish.dev )" deleteAccount: "删除账号" document: "文档" numberOfPageCache: "缓存页数" @@ -1975,6 +1977,8 @@ confirm: 确认 importZip: 导入 ZIP exportZip: 导出 ZIP getQrCode: "获取二维码" +remoteFollow: "远程关注" +remoteFollowUrl: "远程关注 URL" emojiPackCreator: 表情包创建工具 objectStorageS3ForcePathStyleDesc: 打开此选项可构建格式为 "s3.amazonaws.com//" 而非 ".s3.amazonaws.com" 的端点 URL。 diff --git a/packages/client/src/pages/follow-me.vue b/packages/client/src/pages/follow-me.vue new file mode 100644 index 0000000000..c3dd0217cf --- /dev/null +++ b/packages/client/src/pages/follow-me.vue @@ -0,0 +1,78 @@ + + + diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts index 7273919261..769cbd44f7 100644 --- a/packages/client/src/router.ts +++ b/packages/client/src/router.ts @@ -325,6 +325,10 @@ export const routes: RouteDef[] = [ component: page(() => import("./pages/follow.vue")), loginRequired: true, }, + { + path: "/follow-me", + component: page(() => import("./pages/follow-me.vue")), + }, { path: "/authorize_interaction", component: page(() => import("./pages/authorize_interaction.vue")), diff --git a/packages/client/src/scripts/get-user-menu.ts b/packages/client/src/scripts/get-user-menu.ts index 5f6108d008..e7edaec391 100644 --- a/packages/client/src/scripts/get-user-menu.ts +++ b/packages/client/src/scripts/get-user-menu.ts @@ -281,6 +281,13 @@ export function getUserMenu(user, router: Router = mainRouter) { copyToClipboard(`https://${host}/@${user.username}.json`); }, }, + { + icon: `${icon("ph-hand-waving")}`, + text: i18n.ts.remoteFollowUrl, + action: () => { + copyToClipboard(`https://${host}/follow-me?acct=${user.username}`); + }, + }, ], }, { @@ -290,13 +297,20 @@ export function getUserMenu(user, router: Router = mainRouter) { os.post({ specified: user }); }, }, + !isSignedIn(me) + ? { + icon: `${icon("ph-hand-waving")}`, + text: i18n.ts.remoteFollow, + action: () => { + router.push(`/follow-me?acct=${user.username}`); + }, + } + : undefined, { icon: "ph-qr-code ph-bold ph-lg", text: i18n.ts.getQrCode, action: () => { - os.displayQrCode( - `https://${host}/authorize-follow?acct=${user.username}`, - ); + os.displayQrCode(`https://${host}/follow-me?acct=${user.username}`); }, }, isSignedIn(me) && me.id !== user.id From 320f933e9d5a7e15c87c14d66cfec2b858d4e66c Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 01:04:40 +0900 Subject: [PATCH 12/41] chore (backend): to be on the safe side --- .../src/remote/activitypub/models/note.ts | 19 ++++++++++++------- .../server/api/endpoints/admin/emoji/add.ts | 16 ++++++++++++---- .../server/api/endpoints/admin/emoji/copy.ts | 17 +++++++++++++---- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index 7f0c8c820d..2d847ef968 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -493,11 +493,16 @@ export async function extractEmojis( tag.icon!.url !== exists.originalUrl || !(exists.width && exists.height) ) { - let size: ImageSize = { width: 0, height: 0 }; - try { - size = await getImageSizeFromUrl(tag.icon!.url); - } catch { - /* skip if any error happens */ + let size: ImageSize | null = null; + if (tag.icon?.url != null) { + try { + size = await getImageSizeFromUrl(tag.icon.url); + } catch (err) { + apLogger.info( + `Failed to determine the size of the image: ${tag.icon.url}`, + ); + apLogger.debug(inspect(err)); + } } await Emojis.update( { @@ -509,8 +514,8 @@ export async function extractEmojis( originalUrl: tag.icon!.url, publicUrl: tag.icon!.url, updatedAt: new Date(), - width: size.width || null, - height: size.height || null, + width: size?.width || null, + height: size?.height || null, }, ); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 0224e19b04..5408c9dc7a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -1,11 +1,13 @@ import define from "@/server/api/define.js"; import { Emojis, DriveFiles } from "@/models/index.js"; -import { genId, getImageSizeFromUrl } from "backend-rs"; +import { type ImageSize, genId, getImageSizeFromUrl } from "backend-rs"; import { insertModerationLog } from "@/services/insert-moderation-log.js"; import { ApiError } from "@/server/api/error.js"; import rndstr from "rndstr"; import { publishBroadcastStream } from "@/services/stream.js"; import { db } from "@/db/postgre.js"; +import { apiLogger } from "@/server/api/logger.js"; +import { inspect } from "node:util"; export const meta = { tags: ["admin", "emoji"], @@ -48,7 +50,13 @@ export default define(meta, paramDef, async (ps, me) => { ? file.name.split(".")[0] : `_${rndstr("a-z0-9", 8)}_`; - const size = await getImageSizeFromUrl(file.url); + let size: ImageSize | null = null; + try { + size = await getImageSizeFromUrl(file.url); + } catch (err) { + apiLogger.info(`Failed to determine the image size: ${file.url}`); + apiLogger.debug(inspect(err)); + } const emoji = await Emojis.insert({ id: genId(), @@ -61,8 +69,8 @@ export default define(meta, paramDef, async (ps, me) => { publicUrl: file.webpublicUrl ?? file.url, type: file.webpublicType ?? file.type, license: null, - width: size.width || null, - height: size.height || null, + width: size?.width || null, + height: size?.height || null, }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); await db.queryResultCache!.remove(["meta_emojis"]); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index b5f67becdf..ded768f2cd 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -1,11 +1,13 @@ import define from "@/server/api/define.js"; import { Emojis } from "@/models/index.js"; -import { genId, getImageSizeFromUrl } from "backend-rs"; +import { type ImageSize, genId, getImageSizeFromUrl } from "backend-rs"; import { ApiError } from "@/server/api/error.js"; import type { DriveFile } from "@/models/entities/drive-file.js"; import { uploadFromUrl } from "@/services/drive/upload-from-url.js"; import { publishBroadcastStream } from "@/services/stream.js"; import { db } from "@/db/postgre.js"; +import { apiLogger } from "@/server/api/logger"; +import { inspect } from "node:util"; export const meta = { tags: ["admin", "emoji"], @@ -75,7 +77,14 @@ export default define(meta, paramDef, async (ps, me) => { throw new ApiError(); } - const size = await getImageSizeFromUrl(driveFile.url); + let size: ImageSize | null = null; + + try { + size = await getImageSizeFromUrl(driveFile.url); + } catch (err) { + apiLogger.info(`Failed to determine the image size: ${driveFile.url}`); + apiLogger.debug(inspect(err)); + } const copied = await Emojis.insert({ id: genId(), @@ -87,8 +96,8 @@ export default define(meta, paramDef, async (ps, me) => { publicUrl: driveFile.webpublicUrl ?? driveFile.url, type: driveFile.webpublicType ?? driveFile.type, license: emoji.license, - width: size.width || null, - height: size.height || null, + width: size?.width ?? null, + height: size?.height ?? null, }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); await db.queryResultCache!.remove(["meta_emojis"]); From 13b648f6bf2074cb3ce61d17f1ad7ea73f9f781c Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 01:27:23 +0900 Subject: [PATCH 13/41] fix (backend-rs): use proxy and proxyBypassHosts config --- .../backend-rs/src/misc/get_image_size.rs | 17 ++----------- packages/backend-rs/src/util/http_client.rs | 24 +++++++++++++++++++ packages/backend-rs/src/util/mod.rs | 3 +++ 3 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 packages/backend-rs/src/util/http_client.rs diff --git a/packages/backend-rs/src/misc/get_image_size.rs b/packages/backend-rs/src/misc/get_image_size.rs index 2157d7cf3d..f014c62614 100644 --- a/packages/backend-rs/src/misc/get_image_size.rs +++ b/packages/backend-rs/src/misc/get_image_size.rs @@ -1,9 +1,8 @@ use crate::misc::redis_cache::{get_cache, set_cache, CacheError}; +use crate::util::http_client; use image::{io::Reader, ImageError, ImageFormat}; use nom_exif::{parse_jpeg_exif, EntryValue, ExifTag}; -use once_cell::sync::OnceCell; use std::io::Cursor; -use std::time::Duration; use tokio::sync::Mutex; #[derive(thiserror::Error, Debug)] @@ -35,18 +34,6 @@ const BROWSER_SAFE_IMAGE_TYPES: [ImageFormat; 8] = [ ImageFormat::Avif, ]; -static CLIENT: OnceCell = OnceCell::new(); - -fn client() -> Result { - CLIENT - .get_or_try_init(|| { - reqwest::Client::builder() - .timeout(Duration::from_secs(5)) - .build() - }) - .cloned() -} - static MTX_GUARD: Mutex<()> = Mutex::const_new(()); #[derive(Debug, PartialEq)] @@ -78,7 +65,7 @@ pub async fn get_image_size_from_url(url: &str) -> Result { tracing::info!("retrieving image size from {}", url); - let image_bytes = client()?.get(url).send().await?.bytes().await?; + let image_bytes = http_client()?.get(url).send().await?.bytes().await?; let reader = Reader::new(Cursor::new(&image_bytes)).with_guessed_format()?; let format = reader.format(); diff --git a/packages/backend-rs/src/util/http_client.rs b/packages/backend-rs/src/util/http_client.rs new file mode 100644 index 0000000000..143e1a2885 --- /dev/null +++ b/packages/backend-rs/src/util/http_client.rs @@ -0,0 +1,24 @@ +use crate::config::CONFIG; +use once_cell::sync::OnceCell; +use reqwest::{Client, Error, NoProxy, Proxy}; +use std::time::Duration; + +static CLIENT: OnceCell = OnceCell::new(); + +pub fn http_client() -> Result { + CLIENT + .get_or_try_init(|| { + let mut builder = Client::builder().timeout(Duration::from_secs(5)); + + if let Some(proxy_url) = &CONFIG.proxy { + let mut proxy = Proxy::all(proxy_url)?; + if let Some(proxy_bypass_hosts) = &CONFIG.proxy_bypass_hosts { + proxy = proxy.no_proxy(NoProxy::from_string(&proxy_bypass_hosts.join(","))); + } + builder = builder.proxy(proxy); + } + + builder.build() + }) + .cloned() +} diff --git a/packages/backend-rs/src/util/mod.rs b/packages/backend-rs/src/util/mod.rs index 1be5a7fd1f..13fb8ee64b 100644 --- a/packages/backend-rs/src/util/mod.rs +++ b/packages/backend-rs/src/util/mod.rs @@ -1,2 +1,5 @@ +pub use http_client::http_client; + +pub mod http_client; pub mod id; pub mod random; From 3b172a776266e7bdec6814995e9c0815da845e34 Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 01:30:16 +0900 Subject: [PATCH 14/41] chore (backend-rs): fix typo --- packages/backend-rs/src/misc/get_image_size.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend-rs/src/misc/get_image_size.rs b/packages/backend-rs/src/misc/get_image_size.rs index f014c62614..9c55846424 100644 --- a/packages/backend-rs/src/misc/get_image_size.rs +++ b/packages/backend-rs/src/misc/get_image_size.rs @@ -9,8 +9,8 @@ use tokio::sync::Mutex; pub enum Error { #[error("Redis cache error: {0}")] CacheErr(#[from] CacheError), - #[error("Reqewst error: {0}")] - ReqewstErr(#[from] reqwest::Error), + #[error("Reqwest error: {0}")] + ReqwestErr(#[from] reqwest::Error), #[error("Image decoding error: {0}")] ImageErr(#[from] ImageError), #[error("Image decoding error: {0}")] From fd40c3dd2e86bae70bb8f3c19045c7e158e2770a Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 01:49:11 +0900 Subject: [PATCH 15/41] locale: update en-US.yml --- locales/en-US.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index 72d660e273..12f43ee4ba 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1012,7 +1012,7 @@ isSystemAccount: "This account is created and automatically operated by the syst it may break your server." typeToConfirm: "Please enter {x} to confirm" useThisAccountConfirm: "Do you want to continue with this account?" -inputAccountId: "Please input your account (e.g., @firefish@info.firefish.dev )" +inputAccountId: "Please input your account (e.g., @firefish@info.firefish.dev)" deleteAccount: "Delete account" document: "Documentation" numberOfPageCache: "Number of cached pages" @@ -1160,8 +1160,8 @@ confirm: "Confirm" importZip: "Import ZIP" exportZip: "Export ZIP" getQrCode: "Get QR code" -remoteFollow: "Remote Follow" -remoteFollowUrl: "Remote Follow URL" +remoteFollow: "Remote follow" +remoteFollowUrl: "Remote follow URL" emojiPackCreator: "Emoji pack creator" indexable: "Indexable" indexableDescription: "Allow built-in search to show your public posts" From 171e2f3973e6c5ad77edfbaa5e8172a4e7a32400 Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 01:56:43 +0900 Subject: [PATCH 16/41] chore: update lockfile --- pnpm-lock.yaml | 85 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16a4ad6ad4..b316aacb53 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -593,6 +593,9 @@ importers: '@types/punycode': specifier: 2.1.4 version: 2.1.4 + '@types/qrcode': + specifier: 1.5.1 + version: 1.5.1 '@types/seedrandom': specifier: 3.0.8 version: 3.0.8 @@ -692,6 +695,9 @@ importers: libopenmpt-wasm: specifier: github:TheEssem/libopenmpt-packaging#build version: github.com/TheEssem/libopenmpt-packaging/d05d151a72b638c6312227af0417aca69521172c + long: + specifier: ^5.2.3 + version: 5.2.3 matter-js: specifier: 0.19.0 version: 0.19.0 @@ -701,6 +707,9 @@ importers: moment: specifier: 2.30.1 version: 2.30.1 + multer: + specifier: 1.4.4-lts.1 + version: 1.4.4-lts.1 photoswipe: specifier: 5.4.3 version: 5.4.3 @@ -710,6 +719,12 @@ importers: punycode: specifier: 2.3.1 version: 2.3.1 + qrcode: + specifier: 1.5.3 + version: 1.5.3 + qrcode-vue3: + specifier: ^1.6.8 + version: 1.6.8 rollup: specifier: 4.14.2 version: 4.14.2 @@ -819,7 +834,7 @@ importers: version: 9.3.1 ts-jest: specifier: ^29.1.2 - version: 29.1.2(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.4.5) + version: 29.1.2(@babel/core@7.24.4)(jest@29.7.0)(typescript@5.4.5) ts-node: specifier: 10.9.2 version: 10.9.2(@swc/core@1.4.13)(@types/node@20.12.7)(typescript@5.4.5) @@ -931,7 +946,7 @@ importers: version: 4.17.21 ts-jest: specifier: ^29.0.5 - version: 29.1.1(@babel/core@7.24.4)(jest@29.7.0)(typescript@4.9.4) + version: 29.1.1(@babel/core@7.23.2)(jest@29.7.0)(typescript@4.9.4) typedoc: specifier: ^0.23.24 version: 0.23.28(typescript@4.9.4) @@ -4381,6 +4396,12 @@ packages: resolution: {integrity: sha512-trzh6NzBnq8yw5e35f8xe8VTYjqM3NE7bohBtvDVf/dtUer3zYTLK1Ka3DG3p7bdtoaOHZucma6FfVKlQ134pQ==} dev: true + /@types/qrcode@1.5.1: + resolution: {integrity: sha512-HpSN675K0PmxIDRpjMI3Mc2GiKo3dNu+X/F5SoItiaDS1lVfgC6Wac1c5lQDfKWbTJUSHWiHKzpJpBZG7k9gaA==} + dependencies: + '@types/node': 20.12.7 + dev: true + /@types/qrcode@1.5.5: resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==} dependencies: @@ -5419,7 +5440,6 @@ packages: /append-field@1.0.0: resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} - dev: false /aproba@2.0.0: resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} @@ -6290,7 +6310,6 @@ packages: engines: {node: '>=10.16.0'} dependencies: streamsearch: 1.1.0 - dev: false /bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} @@ -6744,7 +6763,6 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 6.2.0 - dev: false /cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} @@ -6987,7 +7005,6 @@ packages: inherits: 2.0.4 readable-stream: 2.3.8 typedarray: 0.0.6 - dev: false /concordance@5.0.4: resolution: {integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==} @@ -7266,7 +7283,6 @@ packages: /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: false /crc-32@1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} @@ -7804,7 +7820,6 @@ packages: /dijkstrajs@1.0.3: resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} - dev: false /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} @@ -7987,7 +8002,6 @@ packages: /encode-utf8@1.0.3: resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} - dev: false /encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} @@ -11121,7 +11135,6 @@ packages: /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: false /isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -12507,6 +12520,10 @@ packages: engines: {node: '>= 0.6.0'} dev: false + /long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + dev: true + /lowercase-keys@2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} @@ -12679,7 +12696,6 @@ packages: /media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} - dev: false /memoize@10.0.0: resolution: {integrity: sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA==} @@ -12905,7 +12921,6 @@ packages: hasBin: true dependencies: minimist: 1.2.8 - dev: false /mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} @@ -13005,6 +13020,19 @@ packages: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} dev: true + /multer@1.4.4-lts.1: + resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==} + engines: {node: '>= 6.0.0'} + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 1.6.2 + mkdirp: 0.5.6 + object-assign: 4.1.1 + type-is: 1.6.18 + xtend: 4.0.2 + dev: true + /multer@1.4.5-lts.1: resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==} engines: {node: '>= 6.0.0'} @@ -14036,7 +14064,6 @@ packages: /pngjs@5.0.0: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} - dev: false /pngjs@7.0.0: resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} @@ -14642,6 +14669,16 @@ packages: engines: {node: '>=0.6.0', teleport: '>=0.2.0'} dev: false + /qrcode-generator@1.4.4: + resolution: {integrity: sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==} + dev: true + + /qrcode-vue3@1.6.8: + resolution: {integrity: sha512-LtMnwKWi58ZqHbXBcsTF/VxDYhI6RrBIrDQw8fbDVlO8p5tJBZa7TaIaVYLY937vKO2WCEBmOKksGlpm5ccEIg==} + dependencies: + qrcode-generator: 1.4.4 + dev: true + /qrcode@1.5.3: resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==} engines: {node: '>=10.13.0'} @@ -14651,7 +14688,6 @@ packages: encode-utf8: 1.0.3 pngjs: 5.0.0 yargs: 15.4.1 - dev: false /qs@6.12.1: resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==} @@ -14783,7 +14819,6 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: false /readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} @@ -15024,7 +15059,6 @@ packages: /require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - dev: false /resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} @@ -15204,7 +15238,6 @@ packages: /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: false /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -15746,7 +15779,6 @@ packages: /streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} - dev: false /streamx@2.15.1: resolution: {integrity: sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==} @@ -15836,7 +15868,6 @@ packages: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 - dev: false /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -16398,7 +16429,7 @@ packages: typescript: 5.4.5 dev: true - /ts-jest@29.1.1(@babel/core@7.24.4)(jest@29.7.0)(typescript@4.9.4): + /ts-jest@29.1.1(@babel/core@7.23.2)(jest@29.7.0)(typescript@4.9.4): resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -16419,7 +16450,7 @@ packages: esbuild: optional: true dependencies: - '@babel/core': 7.24.4 + '@babel/core': 7.23.2 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 jest: 29.7.0(@types/node@18.11.18) @@ -16432,7 +16463,7 @@ packages: yargs-parser: 21.1.1 dev: true - /ts-jest@29.1.2(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.4.5): + /ts-jest@29.1.2(@babel/core@7.24.4)(jest@29.7.0)(typescript@5.4.5): resolution: {integrity: sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==} engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true @@ -16453,7 +16484,7 @@ packages: esbuild: optional: true dependencies: - '@babel/core': 7.23.2 + '@babel/core': 7.24.4 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 jest: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) @@ -16646,7 +16677,6 @@ packages: dependencies: media-typer: 0.3.0 mime-types: 2.1.35 - dev: false /type@1.2.0: resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} @@ -16702,7 +16732,6 @@ packages: /typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - dev: false /typedoc@0.23.28(typescript@4.9.4): resolution: {integrity: sha512-9x1+hZWTHEQcGoP7qFmlo4unUoVJLB0H/8vfO/7wqTnZxg4kPuji9y3uRzEu0ZKez63OJAUmiGhUrtukC6Uj3w==} @@ -17465,7 +17494,6 @@ packages: /which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - dev: false /which-typed-array@1.1.11: resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} @@ -17536,7 +17564,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: false /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} @@ -17640,7 +17667,6 @@ packages: /xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} - dev: false /y18n@3.2.2: resolution: {integrity: sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==} @@ -17648,7 +17674,6 @@ packages: /y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - dev: false /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} @@ -17693,7 +17718,6 @@ packages: dependencies: camelcase: 5.3.1 decamelize: 1.2.0 - dev: false /yargs-parser@20.2.4: resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} @@ -17740,7 +17764,6 @@ packages: which-module: 2.0.1 y18n: 4.0.3 yargs-parser: 18.1.3 - dev: false /yargs@16.2.0: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} From 2d15d6dcfa6cd04053d9100271510ed02aab4e07 Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 02:03:15 +0900 Subject: [PATCH 17/41] refactor (client): clean up conditions a bit --- packages/client/src/pages/follow-me.vue | 84 ++++++++++++------------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/packages/client/src/pages/follow-me.vue b/packages/client/src/pages/follow-me.vue index c3dd0217cf..80fb6de5a2 100644 --- a/packages/client/src/pages/follow-me.vue +++ b/packages/client/src/pages/follow-me.vue @@ -15,64 +15,60 @@ if (acctUri == null) { throw new Error("acct required"); } -let useThisAccount = isSignedIn(me) ? true : false; - // If the user is already logged in, ask whether to follow using the current account. -if (useThisAccount) { +if (isSignedIn(me)) { const { canceled } = await os.confirm({ type: "question", text: i18n.ts.useThisAccountConfirm, }); + + // use the current account if (!canceled) { waiting(); window.location.href = `/authorize-follow?acct=${acctUri}`; - } else { - useThisAccount = false; } } -if (!useThisAccount) { - // Ask the user what the account ID is - const remoteAccountId = await os.inputText({ - text: i18n.ts.inputAccountId, - }); +// Otherwise ask the user what the other account ID is +const remoteAccountId = await os.inputText({ + text: i18n.ts.inputAccountId, +}); - // If the user do not want enter uri, the user will be redirected to the user page. - if (!remoteAccountId.result) { +// If the user do not want enter uri, the user will be redirected to the user page. +if (!remoteAccountId.result) { + waiting(); + window.location.href = `/@${acctUri}`; +} else { + const remoteAcctInfo = acct.parse(remoteAccountId.result); + + // If the user on this server, redirect directly + if (remoteAcctInfo.host === hostRaw || remoteAcctInfo.host === null) { waiting(); - window.location.href = `/@${acctUri}`; + window.location.href = `/authorize-follow?acct=${acctUri}`; } else { - const remoteAcctInfo = acct.parse(remoteAccountId.result); - - // If the user on this server, redirect directly - if (remoteAcctInfo.host === hostRaw || remoteAcctInfo.host === null) { - waiting(); - window.location.href = `/authorize-follow?acct=${acctUri}`; - } else { - waiting(); - // If not, find the interaction url through webfinger interface - fetch( - `https://${remoteAcctInfo.host}/.well-known/webfinger?resource=${remoteAcctInfo.username}@${remoteAcctInfo.host}`, - { - method: "GET", - }, - ) - .then((response) => response.json()) - .then((data) => { - const subscribeUri = data.links.find( - (link) => link.rel === "http://ostatus.org/schema/1.0/subscribe", - ).template; - window.location.href = subscribeUri.replace( - "{uri}", - acctUri.includes("@") ? acctUri : `${acctUri}@${hostRaw}`, - ); - }) - .catch((e) => { - // TODO: It would be better to provide more information, but the priority of - // waiting component is too high and the pop-up window will be blocked. - window.location.href = `/@${acctUri}`; - }); - } + waiting(); + // If not, find the interaction url through webfinger interface + fetch( + `https://${remoteAcctInfo.host}/.well-known/webfinger?resource=${remoteAcctInfo.username}@${remoteAcctInfo.host}`, + { + method: "GET", + }, + ) + .then((response) => response.json()) + .then((data) => { + const subscribeUri = data.links.find( + (link: { rel: string; }) => link.rel === "http://ostatus.org/schema/1.0/subscribe", + ).template; + window.location.href = subscribeUri.replace( + "{uri}", + acctUri.includes("@") ? acctUri : `${acctUri}@${hostRaw}`, + ); + }) + .catch((_) => { + // TODO: It would be better to provide more information, but the priority of + // waiting component is too high and the pop-up window will be blocked. + window.location.href = `/@${acctUri}`; + }); } } From 0512dca83ca2b42fadfe75ff5b94daf81a7ed19f Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 03:22:07 +0900 Subject: [PATCH 18/41] fix (backend): import .js --- packages/backend/src/server/api/endpoints/admin/emoji/copy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index ded768f2cd..ec8701a6c5 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -6,7 +6,7 @@ import type { DriveFile } from "@/models/entities/drive-file.js"; import { uploadFromUrl } from "@/services/drive/upload-from-url.js"; import { publishBroadcastStream } from "@/services/stream.js"; import { db } from "@/db/postgre.js"; -import { apiLogger } from "@/server/api/logger"; +import { apiLogger } from "@/server/api/logger.js"; import { inspect } from "node:util"; export const meta = { From 79ab7bf7870b559a613f0454e9999b8878a883c5 Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 05:44:37 +0900 Subject: [PATCH 19/41] refactor (backend): port publishAdminStream to backend-rs --- packages/backend-rs/index.d.ts | 7 +++++ packages/backend-rs/index.js | 3 ++- packages/backend-rs/src/service/stream.rs | 9 ++++--- .../src/service/stream/moderation.rs | 21 +++++++++++++++ .../api/endpoints/users/report-abuse.ts | 7 ++--- packages/backend/src/services/stream.ts | 27 ++++++++++--------- 6 files changed, 51 insertions(+), 23 deletions(-) create mode 100644 packages/backend-rs/src/service/stream/moderation.rs diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts index f2cecae0fa..1f5fa5f55c 100644 --- a/packages/backend-rs/index.d.ts +++ b/packages/backend-rs/index.d.ts @@ -1156,6 +1156,13 @@ export enum ChatEvent { Typing = 'typing' } export function publishToChatStream(senderUserId: string, receiverUserId: string, kind: ChatEvent, object: any): void +export interface AbuseUserReportLike { + id: string + targetUserId: string + reporterId: string + comment: string +} +export function publishToModerationStream(moderatorId: string, report: AbuseUserReportLike): void export function getTimestamp(id: string): number /** * The generated ID results in the form of `[8 chars timestamp] + [cuid2]`. diff --git a/packages/backend-rs/index.js b/packages/backend-rs/index.js index aa3776645c..eef6310871 100644 --- a/packages/backend-rs/index.js +++ b/packages/backend-rs/index.js @@ -310,7 +310,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, ChatEvent, publishToChatStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding +const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, ChatEvent, publishToChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding module.exports.SECOND = SECOND module.exports.MINUTE = MINUTE @@ -367,6 +367,7 @@ module.exports.watchNote = watchNote module.exports.unwatchNote = unwatchNote module.exports.ChatEvent = ChatEvent module.exports.publishToChatStream = publishToChatStream +module.exports.publishToModerationStream = publishToModerationStream module.exports.getTimestamp = getTimestamp module.exports.genId = genId module.exports.genIdAt = genIdAt diff --git a/packages/backend-rs/src/service/stream.rs b/packages/backend-rs/src/service/stream.rs index 46fafa64ba..7d61434081 100644 --- a/packages/backend-rs/src/service/stream.rs +++ b/packages/backend-rs/src/service/stream.rs @@ -1,5 +1,6 @@ pub mod antenna; pub mod chat; +pub mod moderation; use crate::config::CONFIG; use crate::database::redis_conn; @@ -11,8 +12,8 @@ pub enum Stream { Internal, #[strum(serialize = "broadcast")] Broadcast, - #[strum(to_string = "adminStream:{user_id}")] - Admin { user_id: String }, + #[strum(to_string = "adminStream:{moderator_id}")] + Moderation { moderator_id: String }, #[strum(to_string = "user:{user_id}")] User { user_id: String }, #[strum(to_string = "channelStream:{channel_id}")] @@ -86,8 +87,8 @@ mod unit_test { assert_eq!(Stream::Internal.to_string(), "internal"); assert_eq!(Stream::Broadcast.to_string(), "broadcast"); assert_eq!( - Stream::Admin { - user_id: "9tb42br63g5apjcq".to_string() + Stream::Moderation { + moderator_id: "9tb42br63g5apjcq".to_string() } .to_string(), "adminStream:9tb42br63g5apjcq" diff --git a/packages/backend-rs/src/service/stream/moderation.rs b/packages/backend-rs/src/service/stream/moderation.rs new file mode 100644 index 0000000000..576bf9fd21 --- /dev/null +++ b/packages/backend-rs/src/service/stream/moderation.rs @@ -0,0 +1,21 @@ +use crate::service::stream::{publish_to_stream, Error, Stream}; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +#[crate::export(object)] +pub struct AbuseUserReportLike { + pub id: String, + pub target_user_id: String, + pub reporter_id: String, + pub comment: String, +} + +#[crate::export(js_name = "publishToModerationStream")] +pub fn publish(moderator_id: String, report: &AbuseUserReportLike) -> Result<(), Error> { + publish_to_stream( + &Stream::Moderation { moderator_id }, + Some("newAbuseUserReport".to_string()), + Some(serde_json::to_string(report)?), + ) +} diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index fda4aa0bb8..f43cd8ae0f 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -1,10 +1,8 @@ import * as mfm from "mfm-js"; import sanitizeHtml from "sanitize-html"; -import { publishAdminStream } from "@/services/stream.js"; import { AbuseUserReports, UserProfiles, Users } from "@/models/index.js"; -import { genId } from "backend-rs"; +import { genId, publishToModerationStream } from "backend-rs"; import { sendEmail } from "@/services/send-email.js"; -import { fetchMeta } from "backend-rs"; import { getUser } from "@/server/api/common/getters.js"; import { ApiError } from "@/server/api/error.js"; import define from "@/server/api/define.js"; @@ -86,9 +84,8 @@ export default define(meta, paramDef, async (ps, me) => { ], }); - const meta = await fetchMeta(true); for (const moderator of moderators) { - publishAdminStream(moderator.id, "newAbuseUserReport", { + publishToModerationStream(moderator.id, { id: report.id, targetUserId: report.targetUserId, reporterId: report.reporterId, diff --git a/packages/backend/src/services/stream.ts b/packages/backend/src/services/stream.ts index 00d33dbb30..ab56171031 100644 --- a/packages/backend/src/services/stream.ts +++ b/packages/backend/src/services/stream.ts @@ -8,7 +8,7 @@ import { config } from "@/config.js"; import type { Channel } from "@/models/entities/channel.js"; import type { StreamChannels, - AdminStreamTypes, + // AdminStreamTypes, // AntennaStreamTypes, BroadcastTypes, ChannelStreamTypes, @@ -193,17 +193,18 @@ class Publisher { this.publish("notesStream", null, note); }; - public publishAdminStream = ( - userId: User["id"], - type: K, - value?: AdminStreamTypes[K], - ): void => { - this.publish( - `adminStream:${userId}`, - type, - typeof value === "undefined" ? null : value, - ); - }; + /* ported to backend-rs */ + // public publishAdminStream = ( + // userId: User["id"], + // type: K, + // value?: AdminStreamTypes[K], + // ): void => { + // this.publish( + // `adminStream:${userId}`, + // type, + // typeof value === "undefined" ? null : value, + // ); + // }; } const publisher = new Publisher(); @@ -225,4 +226,4 @@ export const publishGroupMessagingStream = publisher.publishGroupMessagingStream; export const publishMessagingIndexStream = publisher.publishMessagingIndexStream; -export const publishAdminStream = publisher.publishAdminStream; +// export const publishAdminStream = publisher.publishAdminStream; From ba8e044f4247fb3c5d53ad9e045b6ca51b799dab Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 05:54:10 +0900 Subject: [PATCH 20/41] chore: format --- packages/client/src/pages/follow-me.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client/src/pages/follow-me.vue b/packages/client/src/pages/follow-me.vue index 80fb6de5a2..77de02d2d6 100644 --- a/packages/client/src/pages/follow-me.vue +++ b/packages/client/src/pages/follow-me.vue @@ -57,7 +57,8 @@ if (!remoteAccountId.result) { .then((response) => response.json()) .then((data) => { const subscribeUri = data.links.find( - (link: { rel: string; }) => link.rel === "http://ostatus.org/schema/1.0/subscribe", + (link: { rel: string }) => + link.rel === "http://ostatus.org/schema/1.0/subscribe", ).template; window.location.href = subscribeUri.replace( "{uri}", From 1cfe3bfb737fd1e613abb460c7285af0d035bdbb Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 06:16:51 +0900 Subject: [PATCH 21/41] refactor (backend): port publishChannelStream to backend-rs --- packages/backend-rs/index.d.ts | 1 + packages/backend-rs/index.js | 3 +- packages/backend-rs/src/service/stream.rs | 1 + .../backend-rs/src/service/stream/channel.rs | 10 +++++++ .../backend/src/server/api/stream/index.ts | 13 +++++---- packages/backend/src/services/stream.ts | 29 ++++++++++--------- 6 files changed, 36 insertions(+), 21 deletions(-) create mode 100644 packages/backend-rs/src/service/stream/channel.rs diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts index 1f5fa5f55c..59943ca111 100644 --- a/packages/backend-rs/index.d.ts +++ b/packages/backend-rs/index.d.ts @@ -1149,6 +1149,7 @@ export interface Webhook { export function initializeRustLogger(): void export function watchNote(watcherId: string, noteAuthorId: string, noteId: string): Promise export function unwatchNote(watcherId: string, noteId: string): Promise +export function publishToChannelStream(channelId: string, userId: string): void export enum ChatEvent { Message = 'message', Read = 'read', diff --git a/packages/backend-rs/index.js b/packages/backend-rs/index.js index eef6310871..cc7f7903c1 100644 --- a/packages/backend-rs/index.js +++ b/packages/backend-rs/index.js @@ -310,7 +310,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, ChatEvent, publishToChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding +const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, publishToChannelStream, ChatEvent, publishToChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding module.exports.SECOND = SECOND module.exports.MINUTE = MINUTE @@ -365,6 +365,7 @@ module.exports.UserProfileMutingnotificationtypesEnum = UserProfileMutingnotific module.exports.initializeRustLogger = initializeRustLogger module.exports.watchNote = watchNote module.exports.unwatchNote = unwatchNote +module.exports.publishToChannelStream = publishToChannelStream module.exports.ChatEvent = ChatEvent module.exports.publishToChatStream = publishToChatStream module.exports.publishToModerationStream = publishToModerationStream diff --git a/packages/backend-rs/src/service/stream.rs b/packages/backend-rs/src/service/stream.rs index 7d61434081..963d11f899 100644 --- a/packages/backend-rs/src/service/stream.rs +++ b/packages/backend-rs/src/service/stream.rs @@ -1,4 +1,5 @@ pub mod antenna; +pub mod channel; pub mod chat; pub mod moderation; diff --git a/packages/backend-rs/src/service/stream/channel.rs b/packages/backend-rs/src/service/stream/channel.rs new file mode 100644 index 0000000000..10a04c5e66 --- /dev/null +++ b/packages/backend-rs/src/service/stream/channel.rs @@ -0,0 +1,10 @@ +use crate::service::stream::{publish_to_stream, Error, Stream}; + +#[crate::export(js_name = "publishToChannelStream")] +pub fn publish(channel_id: String, user_id: String) -> Result<(), Error> { + publish_to_stream( + &Stream::Channel { channel_id }, + Some("typing".to_string()), + Some(format!("\"{}\"", user_id)), + ) +} diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index 97337bd7e1..4079573ffb 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -14,11 +14,12 @@ import { } from "@/models/index.js"; import type { AccessToken } from "@/models/entities/access-token.js"; import type { UserProfile } from "@/models/entities/user-profile.js"; +import { publishGroupMessagingStream } from "@/services/stream.js"; import { - publishChannelStream, - publishGroupMessagingStream, -} from "@/services/stream.js"; -import { publishToChatStream, ChatEvent } from "backend-rs"; + publishToChannelStream, + publishToChatStream, + ChatEvent, +} from "backend-rs"; import type { UserGroup } from "@/models/entities/user-group.js"; import type { Packed } from "@/misc/schema.js"; import { readNotification } from "@/server/api/common/read-notification.js"; @@ -512,9 +513,9 @@ export default class Connection { } } - private typingOnChannel(channel: ChannelModel["id"]) { + private typingOnChannel(channelId: ChannelModel["id"]) { if (this.user) { - publishChannelStream(channel, "typing", this.user.id); + publishToChannelStream(channelId, this.user.id); } } diff --git a/packages/backend/src/services/stream.ts b/packages/backend/src/services/stream.ts index ab56171031..38a15dcfa3 100644 --- a/packages/backend/src/services/stream.ts +++ b/packages/backend/src/services/stream.ts @@ -5,13 +5,13 @@ import type { UserList } from "@/models/entities/user-list.js"; import type { UserGroup } from "@/models/entities/user-group.js"; import { config } from "@/config.js"; // import type { Antenna } from "@/models/entities/antenna.js"; -import type { Channel } from "@/models/entities/channel.js"; +// import type { Channel } from "@/models/entities/channel.js"; import type { StreamChannels, // AdminStreamTypes, // AntennaStreamTypes, BroadcastTypes, - ChannelStreamTypes, + // ChannelStreamTypes, DriveStreamTypes, GroupMessagingStreamTypes, InternalStreamTypes, @@ -110,17 +110,18 @@ class Publisher { }); }; - public publishChannelStream = ( - channelId: Channel["id"], - type: K, - value?: ChannelStreamTypes[K], - ): void => { - this.publish( - `channelStream:${channelId}`, - type, - typeof value === "undefined" ? null : value, - ); - }; + /* ported to backend-rs */ + // public publishChannelStream = ( + // channelId: Channel["id"], + // type: K, + // value?: ChannelStreamTypes[K], + // ): void => { + // this.publish( + // `channelStream:${channelId}`, + // type, + // typeof value === "undefined" ? null : value, + // ); + // }; public publishUserListStream = ( listId: UserList["id"], @@ -218,7 +219,7 @@ export const publishMainStream = publisher.publishMainStream; export const publishDriveStream = publisher.publishDriveStream; export const publishNoteStream = publisher.publishNoteStream; export const publishNotesStream = publisher.publishNotesStream; -export const publishChannelStream = publisher.publishChannelStream; +// export const publishChannelStream = publisher.publishChannelStream; export const publishUserListStream = publisher.publishUserListStream; // export const publishAntennaStream = publisher.publishAntennaStream; // export const publishMessagingStream = publisher.publishMessagingStream; From 8534154792d4cb7b7d12b38a7aac135e8238cdb5 Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 06:30:30 +0900 Subject: [PATCH 22/41] chore (backend-rs): add comment --- packages/backend-rs/src/service/stream/chat.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/backend-rs/src/service/stream/chat.rs b/packages/backend-rs/src/service/stream/chat.rs index ae2efce938..3015d921e1 100644 --- a/packages/backend-rs/src/service/stream/chat.rs +++ b/packages/backend-rs/src/service/stream/chat.rs @@ -13,12 +13,15 @@ pub enum ChatEvent { Typing, } +// We want to merge `kind` and `object` into a single enum +// https://github.com/napi-rs/napi-rs/issues/2036 + #[crate::export(js_name = "publishToChatStream")] pub fn publish( sender_user_id: String, receiver_user_id: String, kind: ChatEvent, - object: &serde_json::Value, // TODO?: change this to enum + object: &serde_json::Value, ) -> Result<(), Error> { publish_to_stream( &Stream::Chat { From 42f704b515489bd4e74df338d968337dab75dad9 Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 06:51:28 +0900 Subject: [PATCH 23/41] chore (backend-rs): remove whitespaces --- packages/backend-rs/src/service/stream.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/backend-rs/src/service/stream.rs b/packages/backend-rs/src/service/stream.rs index 963d11f899..a42ecd08f1 100644 --- a/packages/backend-rs/src/service/stream.rs +++ b/packages/backend-rs/src/service/stream.rs @@ -59,7 +59,7 @@ pub fn publish_to_stream( ) -> Result<(), Error> { let message = if let Some(kind) = kind { format!( - "{{ \"type\": \"{}\", \"body\": {} }}", + "{{\"type\":\"{}\",\"body\":{}}}", kind, value.unwrap_or("null".to_string()), ) @@ -69,10 +69,7 @@ pub fn publish_to_stream( redis_conn()?.publish( &CONFIG.host, - format!( - "{{ \"channel\": \"{}\", \"message\": {} }}", - stream, message, - ), + format!("{{\"channel\":\"{}\",\"message\":{}}}", stream, message), )?; Ok(()) From b58d940e716efc7aca7b3df9e1b9bf7b77a150ac Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 07:13:02 +0900 Subject: [PATCH 24/41] fix (backend-rs): add openssl as a dependency --- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + packages/backend-rs/Cargo.toml | 1 + 3 files changed, 13 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 1e5df0278a..ed3f639cef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,6 +213,7 @@ dependencies = [ "napi-derive", "nom-exif", "once_cell", + "openssl", "pretty_assertions", "rand", "redis", @@ -1818,6 +1819,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-src" +version = "300.2.3+3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.102" @@ -1826,6 +1836,7 @@ checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] diff --git a/Cargo.toml b/Cargo.toml index cda98ba300..14e5d45c40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ idna = "0.5.0" image = "0.25.1" nom-exif = "1.2.0" once_cell = "1.19.0" +openssl = "0.10.64" pretty_assertions = "1.4.0" proc-macro2 = "1.0.79" quote = "1.0.36" diff --git a/packages/backend-rs/Cargo.toml b/packages/backend-rs/Cargo.toml index 8117c14a58..c1b511b992 100644 --- a/packages/backend-rs/Cargo.toml +++ b/packages/backend-rs/Cargo.toml @@ -27,6 +27,7 @@ idna = { workspace = true } image = { workspace = true } nom-exif = { workspace = true } once_cell = { workspace = true } +openssl = { workspace = true, features = ["vendored"] } rand = { workspace = true } redis = { workspace = true } regex = { workspace = true } From f806c47c7c42b17a7895e1d20a701e74f2a7f204 Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 07:26:58 +0900 Subject: [PATCH 25/41] style: move QR code and remote follow URL to another category --- locales/en-US.yml | 4 +-- locales/zh-CN.yml | 2 +- packages/client/src/scripts/get-user-menu.ts | 35 ++++++++++++-------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index 12f43ee4ba..fa2a0b11ce 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1159,9 +1159,9 @@ addRe: "Add \"re:\" at the beginning of comment in reply to a post with a conten confirm: "Confirm" importZip: "Import ZIP" exportZip: "Export ZIP" -getQrCode: "Get QR code" +getQrCode: "Show QR code" remoteFollow: "Remote follow" -remoteFollowUrl: "Remote follow URL" +copyRemoteFollowUrl: "Copy remote follow URL" emojiPackCreator: "Emoji pack creator" indexable: "Indexable" indexableDescription: "Allow built-in search to show your public posts" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 984f7e5831..7642fc80db 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1978,7 +1978,7 @@ importZip: 导入 ZIP exportZip: 导出 ZIP getQrCode: "获取二维码" remoteFollow: "远程关注" -remoteFollowUrl: "远程关注 URL" +copyRemoteFollowUrl: "复制远程关注 URL" emojiPackCreator: 表情包创建工具 objectStorageS3ForcePathStyleDesc: 打开此选项可构建格式为 "s3.amazonaws.com//" 而非 ".s3.amazonaws.com" 的端点 URL。 diff --git a/packages/client/src/scripts/get-user-menu.ts b/packages/client/src/scripts/get-user-menu.ts index e7edaec391..7a3046aad7 100644 --- a/packages/client/src/scripts/get-user-menu.ts +++ b/packages/client/src/scripts/get-user-menu.ts @@ -255,6 +255,27 @@ export function getUserMenu(user, router: Router = mainRouter) { router.push(`/user-info/${user.id}`); }, }, + { + icon: `${icon("ph-share")}`, + text: i18n.ts.share, + type: "parent", + children: [ + { + icon: "ph-qr-code ph-bold ph-lg", + text: i18n.ts.getQrCode, + action: () => { + os.displayQrCode(`https://${host}/follow-me?acct=${user.username}`); + }, + }, + { + icon: `${icon("ph-hand-waving")}`, + text: i18n.ts.copyRemoteFollowUrl, + action: () => { + copyToClipboard(`https://${host}/follow-me?acct=${user.username}`); + }, + }, + ], + }, { icon: `${icon("ph-newspaper")}`, text: i18n.ts._feeds.copyFeed, @@ -281,13 +302,6 @@ export function getUserMenu(user, router: Router = mainRouter) { copyToClipboard(`https://${host}/@${user.username}.json`); }, }, - { - icon: `${icon("ph-hand-waving")}`, - text: i18n.ts.remoteFollowUrl, - action: () => { - copyToClipboard(`https://${host}/follow-me?acct=${user.username}`); - }, - }, ], }, { @@ -306,13 +320,6 @@ export function getUserMenu(user, router: Router = mainRouter) { }, } : undefined, - { - icon: "ph-qr-code ph-bold ph-lg", - text: i18n.ts.getQrCode, - action: () => { - os.displayQrCode(`https://${host}/follow-me?acct=${user.username}`); - }, - }, isSignedIn(me) && me.id !== user.id ? { type: "link", From 18439f6e27737b164b1306eabd8ddece572b8772 Mon Sep 17 00:00:00 2001 From: Salif Mehmed Date: Thu, 25 Apr 2024 18:05:04 +0000 Subject: [PATCH 26/41] locale: update translations (Bulgarian) Currently translated at 44.8% (864 of 1926 strings) Translation: Firefish/locales Translate-URL: https://hosted.weblate.org/projects/firefish/locales/bg/ --- locales/bg-BG.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/locales/bg-BG.yml b/locales/bg-BG.yml index 45bd1eb465..75bb03773d 100644 --- a/locales/bg-BG.yml +++ b/locales/bg-BG.yml @@ -19,7 +19,7 @@ deleteAndEditConfirm: Сигурни ли сте, че искате да изт copyUsername: Копиране на потребителското име searchUser: Търсене на потребител reply: Отговор -showMore: Покажи още +showMore: Показване на повече loadMore: Зареди още followRequestAccepted: Заявката за последване е приета importAndExport: Импорт/експорт на данни @@ -336,6 +336,10 @@ _pages: title: Заглавие my: Моите страници pageSetting: Настройки на страницата + url: Адрес на страницата + summary: Кратко обобщение + alignCenter: Центриране на елементите + variables: Променливи _deck: _columns: notifications: Известия @@ -398,7 +402,7 @@ sendMessage: Изпращане на съобщение jumpToPrevious: Премини към предишно newer: по-ново older: по-старо -showLess: Покажи по-малко +showLess: Показване на по-малко youGotNewFollower: те последва receiveFollowRequest: Заявка за последване получена mention: Споменаване @@ -754,7 +758,7 @@ _feeds: general: Общи metadata: Метаданни disk: Диск -featured: Препоръчани +featured: Препоръчано yearsOld: на {age} години reload: Опресняване invites: Покани @@ -940,3 +944,11 @@ showGapBetweenNotesInTimeline: Показване на празнина межд lookup: Поглеждане media: Мултимедия welcomeBackWithName: Добре дошли отново, {name} +reduceUiAnimation: Намаляване на UI анимациите +clickToFinishEmailVerification: Моля, натиснете [{ok}], за да завършите потвърждаването + на ел. поща. +_cw: + show: Показване на съдържанието +remoteFollow: Отдалечено последване +messagingUnencryptedInfo: Чатовете във Firefish не са шифровани от край до край. Не + споделяйте чувствителна информация през Firefish. From 3190f667404eb8ace27ae70a085a34f8eac84b28 Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 11:49:56 +0900 Subject: [PATCH 27/41] chore (backend): convert TypeORM's simple-array into normal array --- docs/downgrade.sql | 8 +++++ packages/backend-rs/src/model/entity/user.rs | 4 +-- .../migration/1714099399879-alter-aka-type.ts | 36 +++++++++++++++++++ packages/backend/src/models/entities/user.ts | 4 ++- 4 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 packages/backend/src/migration/1714099399879-alter-aka-type.ts diff --git a/docs/downgrade.sql b/docs/downgrade.sql index 44222f818f..eed0079c3b 100644 --- a/docs/downgrade.sql +++ b/docs/downgrade.sql @@ -1,6 +1,7 @@ BEGIN; DELETE FROM "migrations" WHERE name IN ( + 'AlterAkaType1714099399879', 'AddDriveFileUsage1713451569342', 'ConvertCwVarcharToText1713225866247', 'FixChatFileConstraint1712855579316', @@ -24,6 +25,13 @@ DELETE FROM "migrations" WHERE name IN ( 'RemoveNativeUtilsMigration1705877093218' ); +-- alter-aka-type +ALTER TABLE "user" RENAME COLUMN "alsoKnownAs" TO "alsoKnownAsOld"; +ALTER TABLE "user" ADD COLUMN "alsoKnownAs" text; +UPDATE "user" SET "alsoKnownAs" = array_to_string("alsoKnownAsOld", ','); +COMMENT ON COLUMN "user"."alsoKnownAs" IS 'URIs the user is known as too'; +ALTER TABLE "user" DROP COLUMN "alsoKnownAsOld"; + -- AddDriveFileUsage ALTER TABLE "drive_file" DROP COLUMN "usageHint"; DROP TYPE "drive_file_usage_hint_enum"; diff --git a/packages/backend-rs/src/model/entity/user.rs b/packages/backend-rs/src/model/entity/user.rs index f8a2b09324..3ceac93fb5 100644 --- a/packages/backend-rs/src/model/entity/user.rs +++ b/packages/backend-rs/src/model/entity/user.rs @@ -71,14 +71,14 @@ pub struct Model { pub drive_capacity_override_mb: Option, #[sea_orm(column_name = "movedToUri")] pub moved_to_uri: Option, - #[sea_orm(column_name = "alsoKnownAs", column_type = "Text", nullable)] - pub also_known_as: Option, #[sea_orm(column_name = "speakAsCat")] pub speak_as_cat: bool, #[sea_orm(column_name = "emojiModPerm")] pub emoji_mod_perm: UserEmojimodpermEnum, #[sea_orm(column_name = "isIndexable")] pub is_indexable: bool, + #[sea_orm(column_name = "alsoKnownAs")] + pub also_known_as: Option>, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/packages/backend/src/migration/1714099399879-alter-aka-type.ts b/packages/backend/src/migration/1714099399879-alter-aka-type.ts new file mode 100644 index 0000000000..ecb031e32d --- /dev/null +++ b/packages/backend/src/migration/1714099399879-alter-aka-type.ts @@ -0,0 +1,36 @@ +import type { MigrationInterface, QueryRunner } from "typeorm"; + +export class AlterAkaType1714099399879 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "user" RENAME COLUMN "alsoKnownAs" TO "alsoKnownAsOld"`, + ); + await queryRunner.query( + `ALTER TABLE "user" ADD COLUMN "alsoKnownAs" character varying(512)[]`, + ); + await queryRunner.query( + `UPDATE "user" SET "alsoKnownAs" = string_to_array("alsoKnownAsOld", ',')::character varying[]`, + ); + await queryRunner.query( + `UPDATE "user" SET "alsoKnownAs" = NULL WHERE "alsoKnownAs" = '{}'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."alsoKnownAs" IS 'URIs the user is known as too'`, + ); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "alsoKnownAsOld"`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "user" RENAME COLUMN "alsoKnownAs" TO "alsoKnownAsOld"`, + ); + await queryRunner.query(`ALTER TABLE "user" ADD COLUMN "alsoKnownAs" text`); + await queryRunner.query( + `UPDATE "user" SET "alsoKnownAs" = array_to_string("alsoKnownAsOld", ',')`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."alsoKnownAs" IS 'URIs the user is known as too'`, + ); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "alsoKnownAsOld"`); + } +} diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index 152acfedb7..69a2b4dc27 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -88,7 +88,9 @@ export class User { }) public movedToUri: string | null; - @Column("simple-array", { + @Column("varchar", { + length: 512, + array: true, nullable: true, comment: "URIs the user is known as too", }) From 883645a5819f4c22620195f55ed2a5447fa57f8e Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 12:14:34 +0900 Subject: [PATCH 28/41] chore (backend-rs): regenerate index.d.ts --- packages/backend-rs/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts index 59943ca111..fcebc5f20a 100644 --- a/packages/backend-rs/index.d.ts +++ b/packages/backend-rs/index.d.ts @@ -1017,10 +1017,10 @@ export interface User { isDeleted: boolean driveCapacityOverrideMb: number | null movedToUri: string | null - alsoKnownAs: string | null speakAsCat: boolean emojiModPerm: UserEmojimodpermEnum isIndexable: boolean + alsoKnownAs: Array | null } export interface UserGroup { id: string From 0caba566e63724abce8bdea0f1dc1612561aec5b Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 13:11:11 +0900 Subject: [PATCH 29/41] refactor (backend): port publishMessagingIndexStream to backend-rs --- packages/backend-rs/index.d.ts | 5 +++ packages/backend-rs/index.js | 4 ++- packages/backend-rs/src/service/stream.rs | 3 +- .../src/service/stream/chat_index.rs | 26 +++++++++++++++ .../api/common/read-messaging-message.ts | 12 ++++--- .../backend/src/services/messages/create.ts | 28 +++++++++++++--- packages/backend/src/services/stream.ts | 32 +++++++++---------- 7 files changed, 83 insertions(+), 27 deletions(-) create mode 100644 packages/backend-rs/src/service/stream/chat_index.rs diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts index fcebc5f20a..b6042d2682 100644 --- a/packages/backend-rs/index.d.ts +++ b/packages/backend-rs/index.d.ts @@ -1157,6 +1157,11 @@ export enum ChatEvent { Typing = 'typing' } export function publishToChatStream(senderUserId: string, receiverUserId: string, kind: ChatEvent, object: any): void +export enum ChatIndexEvent { + Message = 'message', + Read = 'read' +} +export function publishToChatIndexStream(userId: string, kind: ChatIndexEvent, object: any): void export interface AbuseUserReportLike { id: string targetUserId: string diff --git a/packages/backend-rs/index.js b/packages/backend-rs/index.js index cc7f7903c1..7244999306 100644 --- a/packages/backend-rs/index.js +++ b/packages/backend-rs/index.js @@ -310,7 +310,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, publishToChannelStream, ChatEvent, publishToChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding +const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToModerationStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding module.exports.SECOND = SECOND module.exports.MINUTE = MINUTE @@ -368,6 +368,8 @@ module.exports.unwatchNote = unwatchNote module.exports.publishToChannelStream = publishToChannelStream module.exports.ChatEvent = ChatEvent module.exports.publishToChatStream = publishToChatStream +module.exports.ChatIndexEvent = ChatIndexEvent +module.exports.publishToChatIndexStream = publishToChatIndexStream module.exports.publishToModerationStream = publishToModerationStream module.exports.getTimestamp = getTimestamp module.exports.genId = genId diff --git a/packages/backend-rs/src/service/stream.rs b/packages/backend-rs/src/service/stream.rs index a42ecd08f1..dbf0c621c3 100644 --- a/packages/backend-rs/src/service/stream.rs +++ b/packages/backend-rs/src/service/stream.rs @@ -1,6 +1,7 @@ pub mod antenna; pub mod channel; pub mod chat; +pub mod chat_index; pub mod moderation; use crate::config::CONFIG; @@ -39,7 +40,7 @@ pub enum Stream { #[strum(to_string = "messagingStream:{group_id}")] GroupChat { group_id: String }, #[strum(to_string = "messagingIndexStream:{user_id}")] - MessagingIndex { user_id: String }, + ChatIndex { user_id: String }, } #[derive(thiserror::Error, Debug)] diff --git a/packages/backend-rs/src/service/stream/chat_index.rs b/packages/backend-rs/src/service/stream/chat_index.rs new file mode 100644 index 0000000000..eb64384dca --- /dev/null +++ b/packages/backend-rs/src/service/stream/chat_index.rs @@ -0,0 +1,26 @@ +use crate::service::stream::{publish_to_stream, Error, Stream}; + +#[derive(strum::Display)] +#[crate::export(string_enum = "camelCase")] +pub enum ChatIndexEvent { + #[strum(serialize = "message")] + Message, + #[strum(serialize = "read")] + Read, +} + +// We want to merge `kind` and `object` into a single enum +// https://github.com/napi-rs/napi-rs/issues/2036 + +#[crate::export(js_name = "publishToChatIndexStream")] +pub fn publish( + user_id: String, + kind: ChatIndexEvent, + object: &serde_json::Value, +) -> Result<(), Error> { + publish_to_stream( + &Stream::ChatIndex { user_id }, + Some(kind.to_string()), + Some(serde_json::to_string(object)?), + ) +} diff --git a/packages/backend/src/server/api/common/read-messaging-message.ts b/packages/backend/src/server/api/common/read-messaging-message.ts index 20777c8246..47b956e158 100644 --- a/packages/backend/src/server/api/common/read-messaging-message.ts +++ b/packages/backend/src/server/api/common/read-messaging-message.ts @@ -2,8 +2,12 @@ import { publishMainStream, publishGroupMessagingStream, } from "@/services/stream.js"; -import { publishToChatStream, ChatEvent } from "backend-rs"; -import { publishMessagingIndexStream } from "@/services/stream.js"; +import { + publishToChatStream, + publishToChatIndexStream, + ChatEvent, + ChatIndexEvent, +} from "backend-rs"; import { pushNotification } from "@/services/push-notification.js"; import type { User, IRemoteUser } from "@/models/entities/user.js"; import type { MessagingMessage } from "@/models/entities/messaging-message.js"; @@ -55,7 +59,7 @@ export async function readUserMessagingMessage( // Publish event publishToChatStream(otherpartyId, userId, ChatEvent.Read, messageIds); - publishMessagingIndexStream(userId, "read", messageIds); + publishToChatIndexStream(userId, ChatIndexEvent.Read, messageIds); if (!(await Users.getHasUnreadMessagingMessage(userId))) { // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行 @@ -130,7 +134,7 @@ export async function readGroupMessagingMessage( ids: reads, userId: userId, }); - publishMessagingIndexStream(userId, "read", reads); + publishToChatIndexStream(userId, ChatIndexEvent.Read, reads); if (!(await Users.getHasUnreadMessagingMessage(userId))) { // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行 diff --git a/packages/backend/src/services/messages/create.ts b/packages/backend/src/services/messages/create.ts index 562e92e42a..269d1d0e3c 100644 --- a/packages/backend/src/services/messages/create.ts +++ b/packages/backend/src/services/messages/create.ts @@ -7,10 +7,16 @@ import { Mutings, Users, } from "@/models/index.js"; -import { genId, publishToChatStream, toPuny, ChatEvent } from "backend-rs"; +import { + genId, + publishToChatStream, + publishToChatIndexStream, + toPuny, + ChatEvent, + ChatIndexEvent, +} from "backend-rs"; import type { MessagingMessage } from "@/models/entities/messaging-message.js"; import { - publishMessagingIndexStream, publishMainStream, publishGroupMessagingStream, } from "@/services/stream.js"; @@ -57,7 +63,11 @@ export async function createMessage( ChatEvent.Message, messageObj, ); - publishMessagingIndexStream(message.userId, "message", messageObj); + publishToChatIndexStream( + message.userId, + ChatIndexEvent.Message, + messageObj, + ); publishMainStream(message.userId, "messagingMessage", messageObj); } @@ -69,7 +79,11 @@ export async function createMessage( ChatEvent.Message, messageObj, ); - publishMessagingIndexStream(recipientUser.id, "message", messageObj); + publishToChatIndexStream( + recipientUser.id, + ChatIndexEvent.Message, + messageObj, + ); publishMainStream(recipientUser.id, "messagingMessage", messageObj); } } else if (recipientGroup) { @@ -81,7 +95,11 @@ export async function createMessage( userGroupId: recipientGroup.id, }); for (const joining of joinings) { - publishMessagingIndexStream(joining.userId, "message", messageObj); + publishToChatIndexStream( + joining.userId, + ChatIndexEvent.Message, + messageObj, + ); publishMainStream(joining.userId, "messagingMessage", messageObj); } } diff --git a/packages/backend/src/services/stream.ts b/packages/backend/src/services/stream.ts index 38a15dcfa3..4ee0a62553 100644 --- a/packages/backend/src/services/stream.ts +++ b/packages/backend/src/services/stream.ts @@ -16,7 +16,7 @@ import type { GroupMessagingStreamTypes, InternalStreamTypes, MainStreamTypes, - MessagingIndexStreamTypes, + // MessagingIndexStreamTypes, // MessagingStreamTypes, NoteStreamTypes, UserListStreamTypes, @@ -176,19 +176,20 @@ class Publisher { ); }; - public publishMessagingIndexStream = < - K extends keyof MessagingIndexStreamTypes, - >( - userId: User["id"], - type: K, - value?: MessagingIndexStreamTypes[K], - ): void => { - this.publish( - `messagingIndexStream:${userId}`, - type, - typeof value === "undefined" ? null : value, - ); - }; + /* ported to backend-rs */ + // public publishMessagingIndexStream = < + // K extends keyof MessagingIndexStreamTypes, + // >( + // userId: User["id"], + // type: K, + // value?: MessagingIndexStreamTypes[K], + // ): void => { + // this.publish( + // `messagingIndexStream:${userId}`, + // type, + // typeof value === "undefined" ? null : value, + // ); + // }; public publishNotesStream = (note: Note): void => { this.publish("notesStream", null, note); @@ -225,6 +226,5 @@ export const publishUserListStream = publisher.publishUserListStream; // export const publishMessagingStream = publisher.publishMessagingStream; export const publishGroupMessagingStream = publisher.publishGroupMessagingStream; -export const publishMessagingIndexStream = - publisher.publishMessagingIndexStream; +// export const publishMessagingIndexStream = publisher.publishMessagingIndexStream; // export const publishAdminStream = publisher.publishAdminStream; From d40db1ee7ed150ba4fdc6805d5b32ba5c7cbeb41 Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 13:30:13 +0900 Subject: [PATCH 30/41] refactor (backend): port publishBroadcastStream to backend-rs --- packages/backend-rs/index.d.ts | 12 +++++++++ packages/backend-rs/index.js | 3 ++- packages/backend-rs/src/service/stream.rs | 5 ++-- .../src/service/stream/custom_emoji.rs | 27 +++++++++++++++++++ .../server/api/endpoints/admin/emoji/add.ts | 12 +++++---- .../server/api/endpoints/admin/emoji/copy.ts | 12 +++++---- packages/backend/src/services/stream.ts | 25 ++++++++--------- 7 files changed, 71 insertions(+), 25 deletions(-) create mode 100644 packages/backend-rs/src/service/stream/custom_emoji.rs diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts index b6042d2682..85fd9fa97a 100644 --- a/packages/backend-rs/index.d.ts +++ b/packages/backend-rs/index.d.ts @@ -1162,6 +1162,18 @@ export enum ChatIndexEvent { Read = 'read' } export function publishToChatIndexStream(userId: string, kind: ChatIndexEvent, object: any): void +export interface PackedEmoji { + id: string + aliases: Array + name: string + category: string | null + host: string | null + url: string + license: string | null + width: number | null + height: number | null +} +export function publishToBroadcastStream(emoji: PackedEmoji): void export interface AbuseUserReportLike { id: string targetUserId: string diff --git a/packages/backend-rs/index.js b/packages/backend-rs/index.js index 7244999306..663bf917f9 100644 --- a/packages/backend-rs/index.js +++ b/packages/backend-rs/index.js @@ -310,7 +310,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToModerationStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding +const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToModerationStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding module.exports.SECOND = SECOND module.exports.MINUTE = MINUTE @@ -370,6 +370,7 @@ module.exports.ChatEvent = ChatEvent module.exports.publishToChatStream = publishToChatStream module.exports.ChatIndexEvent = ChatIndexEvent module.exports.publishToChatIndexStream = publishToChatIndexStream +module.exports.publishToBroadcastStream = publishToBroadcastStream module.exports.publishToModerationStream = publishToModerationStream module.exports.getTimestamp = getTimestamp module.exports.genId = genId diff --git a/packages/backend-rs/src/service/stream.rs b/packages/backend-rs/src/service/stream.rs index dbf0c621c3..3f53dea131 100644 --- a/packages/backend-rs/src/service/stream.rs +++ b/packages/backend-rs/src/service/stream.rs @@ -2,6 +2,7 @@ pub mod antenna; pub mod channel; pub mod chat; pub mod chat_index; +pub mod custom_emoji; pub mod moderation; use crate::config::CONFIG; @@ -13,7 +14,7 @@ pub enum Stream { #[strum(serialize = "internal")] Internal, #[strum(serialize = "broadcast")] - Broadcast, + CustomEmoji, #[strum(to_string = "adminStream:{moderator_id}")] Moderation { moderator_id: String }, #[strum(to_string = "user:{user_id}")] @@ -84,7 +85,7 @@ mod unit_test { #[test] fn channel_to_string() { assert_eq!(Stream::Internal.to_string(), "internal"); - assert_eq!(Stream::Broadcast.to_string(), "broadcast"); + assert_eq!(Stream::CustomEmoji.to_string(), "broadcast"); assert_eq!( Stream::Moderation { moderator_id: "9tb42br63g5apjcq".to_string() diff --git a/packages/backend-rs/src/service/stream/custom_emoji.rs b/packages/backend-rs/src/service/stream/custom_emoji.rs new file mode 100644 index 0000000000..21158fc761 --- /dev/null +++ b/packages/backend-rs/src/service/stream/custom_emoji.rs @@ -0,0 +1,27 @@ +use crate::service::stream::{publish_to_stream, Error, Stream}; +use serde::{Deserialize, Serialize}; + +// TODO: define schema type in other place +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +#[crate::export(object)] +pub struct PackedEmoji { + pub id: String, + pub aliases: Vec, + pub name: String, + pub category: Option, + pub host: Option, + pub url: String, + pub license: Option, + pub width: Option, + pub height: Option, +} + +#[crate::export(js_name = "publishToBroadcastStream")] +pub fn publish(emoji: &PackedEmoji) -> Result<(), Error> { + publish_to_stream( + &Stream::CustomEmoji, + Some("emojiAdded".to_string()), + Some(format!("{{\"emoji\":{}}}", serde_json::to_string(emoji)?)), + ) +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 5408c9dc7a..59139904bc 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -1,10 +1,14 @@ import define from "@/server/api/define.js"; import { Emojis, DriveFiles } from "@/models/index.js"; -import { type ImageSize, genId, getImageSizeFromUrl } from "backend-rs"; +import { + type ImageSize, + genId, + getImageSizeFromUrl, + publishToBroadcastStream, +} from "backend-rs"; import { insertModerationLog } from "@/services/insert-moderation-log.js"; import { ApiError } from "@/server/api/error.js"; import rndstr from "rndstr"; -import { publishBroadcastStream } from "@/services/stream.js"; import { db } from "@/db/postgre.js"; import { apiLogger } from "@/server/api/logger.js"; import { inspect } from "node:util"; @@ -75,9 +79,7 @@ export default define(meta, paramDef, async (ps, me) => { await db.queryResultCache!.remove(["meta_emojis"]); - publishBroadcastStream("emojiAdded", { - emoji: await Emojis.pack(emoji.id), - }); + publishToBroadcastStream(await Emojis.pack(emoji)); insertModerationLog(me, "addEmoji", { emojiId: emoji.id, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index ec8701a6c5..9b08076b35 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -1,10 +1,14 @@ import define from "@/server/api/define.js"; import { Emojis } from "@/models/index.js"; -import { type ImageSize, genId, getImageSizeFromUrl } from "backend-rs"; +import { + type ImageSize, + genId, + getImageSizeFromUrl, + publishToBroadcastStream, +} from "backend-rs"; import { ApiError } from "@/server/api/error.js"; import type { DriveFile } from "@/models/entities/drive-file.js"; import { uploadFromUrl } from "@/services/drive/upload-from-url.js"; -import { publishBroadcastStream } from "@/services/stream.js"; import { db } from "@/db/postgre.js"; import { apiLogger } from "@/server/api/logger.js"; import { inspect } from "node:util"; @@ -102,9 +106,7 @@ export default define(meta, paramDef, async (ps, me) => { await db.queryResultCache!.remove(["meta_emojis"]); - publishBroadcastStream("emojiAdded", { - emoji: await Emojis.pack(copied.id), - }); + publishToBroadcastStream(await Emojis.pack(copied)); return { id: copied.id, diff --git a/packages/backend/src/services/stream.ts b/packages/backend/src/services/stream.ts index 4ee0a62553..1a821302ce 100644 --- a/packages/backend/src/services/stream.ts +++ b/packages/backend/src/services/stream.ts @@ -10,7 +10,7 @@ import type { StreamChannels, // AdminStreamTypes, // AntennaStreamTypes, - BroadcastTypes, + // BroadcastTypes, // ChannelStreamTypes, DriveStreamTypes, GroupMessagingStreamTypes, @@ -64,16 +64,17 @@ class Publisher { ); }; - public publishBroadcastStream = ( - type: K, - value?: BroadcastTypes[K], - ): void => { - this.publish( - "broadcast", - type, - typeof value === "undefined" ? null : value, - ); - }; + /* ported to backend-rs */ + // public publishBroadcastStream = ( + // type: K, + // value?: BroadcastTypes[K], + // ): void => { + // this.publish( + // "broadcast", + // type, + // typeof value === "undefined" ? null : value, + // ); + // }; public publishMainStream = ( userId: User["id"], @@ -215,7 +216,7 @@ export default publisher; export const publishInternalEvent = publisher.publishInternalEvent; export const publishUserEvent = publisher.publishUserEvent; -export const publishBroadcastStream = publisher.publishBroadcastStream; +// export const publishBroadcastStream = publisher.publishBroadcastStream; export const publishMainStream = publisher.publishMainStream; export const publishDriveStream = publisher.publishDriveStream; export const publishNoteStream = publisher.publishNoteStream; From 37cf4f8361def2b426582166086dee67c6a02c16 Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 26 Apr 2024 13:51:28 +0900 Subject: [PATCH 31/41] chore (backend-rs): tweak logger config --- packages/backend-rs/src/service/log.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/backend-rs/src/service/log.rs b/packages/backend-rs/src/service/log.rs index 789400b4c2..07966b77ad 100644 --- a/packages/backend-rs/src/service/log.rs +++ b/packages/backend-rs/src/service/log.rs @@ -13,7 +13,7 @@ pub fn initialize_logger() { "info" => Level::INFO, "debug" => Level::DEBUG, "trace" => Level::TRACE, - _ => Level::INFO, + _ => Level::INFO, // Fallback }); } else if let Some(levels) = &CONFIG.log_level { // `logLevel` config is Deprecated @@ -27,13 +27,25 @@ pub fn initialize_logger() { builder = builder.with_max_level(Level::WARN); } else if levels.contains(&"error".to_string()) { builder = builder.with_max_level(Level::ERROR); + } else { + // Fallback + builder = builder.with_max_level(Level::INFO); } } else { // Fallback builder = builder.with_max_level(Level::INFO); }; - let subscriber = builder.with_level(true).pretty().finish(); + let subscriber = builder + .without_time() + .with_level(true) + .with_ansi(true) + .with_target(true) + .with_thread_names(true) + .with_line_number(true) + .log_internal_errors(true) + .compact() + .finish(); tracing::subscriber::set_global_default(subscriber).expect("Failed to initialize the logger"); } From 98048699c14788f2379e01f62cf62ba740662539 Mon Sep 17 00:00:00 2001 From: Linerly Date: Thu, 25 Apr 2024 23:57:10 +0000 Subject: [PATCH 32/41] locale: update translations (Indonesian) Currently translated at 100.0% (1926 of 1926 strings) Translation: Firefish/locales Translate-URL: https://hosted.weblate.org/projects/firefish/locales/id/ --- locales/id-ID.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 4535492e7d..be780444a4 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -1825,6 +1825,7 @@ _notification: reacted: mereaksi postinganmu renoted: memposting ulang postinganmu voted: memilih di angketmu + andCountUsers: dan {count} lebih banyak pengguna {acted} _deck: alwaysShowMainColumn: "Selalu tampilkan kolom utama" columnAlign: "Luruskan kolom" @@ -2267,3 +2268,13 @@ markLocalFilesNsfwByDefaultDescription: Terlepas dari pengaturan ini, pengguna d menghapus sendiri tanda NSFW. Berkas yang ada tidak berpengaruh. noteEditHistory: Riwayat penyuntingan kiriman media: Media +antennaLimit: Jumlah antena maksimum yang dapat dibuat oleh setiap pengguna +showAddFileDescriptionAtFirstPost: Buka formulir secara otomatis untuk menulis deskripsi + ketika mencoba mengirim berkas tanpa deskripsi +remoteFollow: Ikuti jarak jauh +foldNotification: Kelompokkan notifikasi yang sama +getQrCode: Tampilkan kode QR +cannotEditVisibility: Kamu tidak bisa menyunting keterlihatan +useThisAccountConfirm: Apakah kamu ingin melanjutkan dengan akun ini? +inputAccountId: Silakan memasukkan akunmu (misalnya, @firefish@info.firefish.dev) +copyRemoteFollowUrl: Salin URL ikuti jarak jauh From 54f100032e6efc7a3557373d415a311d358047c5 Mon Sep 17 00:00:00 2001 From: naskya Date: Thu, 25 Apr 2024 22:34:58 +0000 Subject: [PATCH 33/41] locale: update translations (Japanese) Currently translated at 100.0% (1926 of 1926 strings) Translation: Firefish/locales Translate-URL: https://hosted.weblate.org/projects/firefish/locales/ja/ --- locales/ja-JP.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index fef91cb81b..7bd3db376d 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1902,6 +1902,7 @@ _notification: reacted: がリアクションしました renoted: がブーストしました voted: が投票しました + andCountUsers: と{count}人が{acted}しました _deck: alwaysShowMainColumn: "常にメインカラムを表示" columnAlign: "カラムの寄せ" @@ -2059,3 +2060,10 @@ markLocalFilesNsfwByDefaultDescription: この設定が有効でも、ユーザ noteEditHistory: 編集履歴 showAddFileDescriptionAtFirstPost: 説明の無い添付ファイルを投稿しようとした際に説明を書く画面を自動で開く antennaLimit: 各ユーザーが作れるアンテナの最大数 +inputAccountId: 'あなたのアカウントを入力してください(例: @firefish@info.firefish.dev)' +remoteFollow: リモートフォロー +cannotEditVisibility: 公開範囲は変更できません +useThisAccountConfirm: このアカウントで操作を続けますか? +getQrCode: QRコードを表示 +copyRemoteFollowUrl: リモートからフォローするURLをコピー +foldNotification: 同じ種類の通知をまとめて表示する From 564eb08386cf20fe7cdd88c48f911176ce5bfc3d Mon Sep 17 00:00:00 2001 From: Lhcfl Date: Fri, 26 Apr 2024 22:39:58 +0800 Subject: [PATCH 34/41] feat: rewrite MkPagination for fold --- .../src/components/MkNotificationFolded.vue | 9 +- .../client/src/components/MkNotifications.vue | 18 +- .../client/src/components/MkPagination.vue | 459 +++++++++--------- packages/client/src/components/MkTimeline.vue | 2 +- packages/client/src/scripts/fold.ts | 57 +-- packages/client/src/scripts/scroll.ts | 3 + 6 files changed, 274 insertions(+), 274 deletions(-) diff --git a/packages/client/src/components/MkNotificationFolded.vue b/packages/client/src/components/MkNotificationFolded.vue index f3b9769e84..4f5048555c 100644 --- a/packages/client/src/components/MkNotificationFolded.vue +++ b/packages/client/src/components/MkNotificationFolded.vue @@ -26,7 +26,6 @@ : notification.reaction " :custom-emojis="notification.note.emojis" - :no-style="true" /> -import { onMounted, onUnmounted, ref } from "vue"; +import { computed, onMounted, onUnmounted, ref } from "vue"; import type { Connection } from "firefish-js/src/streaming"; import type { Channels } from "firefish-js/src/streaming.types"; import XReactionIcon from "@/components/MkReactionIcon.vue"; @@ -116,8 +115,10 @@ const defaultReaction = ["⭐", "👍", "❤️"].includes(instance.defaultReact ? instance.defaultReaction : "⭐"; -const users = ref(props.notification.users.slice(0, 5)); -const userleft = ref(props.notification.users.length - users.value.length); +const users = computed(() => props.notification.users.slice(0, 5)); +const userleft = computed( + () => props.notification.users.length - users.value.length, +); let readObserver: IntersectionObserver | undefined; let connection: Connection | null = null; diff --git a/packages/client/src/components/MkNotifications.vue b/packages/client/src/components/MkNotifications.vue index 919a064183..4d34707c17 100644 --- a/packages/client/src/components/MkNotifications.vue +++ b/packages/client/src/components/MkNotifications.vue @@ -1,5 +1,9 @@