Merge branch 'develop' into 'main'
release: v20240809 Co-authored-by: masaya.suzuki <suzukimasaya428@gmail.com> Co-authored-by: GitLab CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev> Co-authored-by: Hosted Weblate <hosted@weblate.org> Co-authored-by: jolupa <jolupameister@gmail.com> Co-authored-by: mester yui <oscarodriguez56@gmail.com> Co-authored-by: Eana Hufwe <eana@1a23.com> See merge request firefish/firefish!11262
This commit is contained in:
commit
1fcc94727a
508 changed files with 6591 additions and 4446 deletions
|
@ -1,4 +1,4 @@
|
|||
image: docker.io/rust:slim-bookworm
|
||||
image: docker.io/rust:bookworm
|
||||
|
||||
services:
|
||||
- name: docker.io/groonga/pgroonga:latest-alpine-12-slim
|
||||
|
@ -47,8 +47,7 @@ variables:
|
|||
|
||||
default:
|
||||
before_script:
|
||||
- apt-get update && apt-get -y upgrade
|
||||
- apt-get -y --no-install-recommends install curl
|
||||
- apt-get update && apt-get -y --no-install-recommends install curl
|
||||
- curl -fsSL 'https://deb.nodesource.com/setup_18.x' | bash -
|
||||
- apt-get install -y --no-install-recommends build-essential clang mold python3 perl nodejs postgresql-client
|
||||
- corepack enable
|
||||
|
@ -111,8 +110,7 @@ test:build:backend_ts:
|
|||
- packages/firefish-js/**/*
|
||||
when: always
|
||||
before_script:
|
||||
- apt-get update && apt-get -y upgrade
|
||||
- apt-get -y --no-install-recommends install curl
|
||||
- apt-get update && apt-get -y --no-install-recommends install curl
|
||||
- curl -fsSL 'https://deb.nodesource.com/setup_18.x' | bash -
|
||||
- apt-get install -y --no-install-recommends build-essential clang mold python3 perl nodejs postgresql-client
|
||||
- corepack enable
|
||||
|
@ -159,8 +157,7 @@ test:build:client:
|
|||
when: always
|
||||
services: []
|
||||
before_script:
|
||||
- apt-get update && apt-get -y upgrade
|
||||
- apt-get -y --no-install-recommends install curl
|
||||
- apt-get update && apt-get -y --no-install-recommends install curl
|
||||
- curl -fsSL 'https://deb.nodesource.com/setup_18.x' | bash -
|
||||
- apt-get install -y --no-install-recommends build-essential python3 perl nodejs
|
||||
- corepack enable
|
||||
|
@ -179,10 +176,9 @@ build:container:
|
|||
variables:
|
||||
STORAGE_DRIVER: overlay
|
||||
before_script:
|
||||
- apt-get update && apt-get -y upgrade
|
||||
- apt-get update && apt-get install -y --no-install-recommends ca-certificates fuse-overlayfs buildah
|
||||
- |-
|
||||
sed -i -r 's/"version": "([-0-9]+)",/"version": "\1-dev",/' package.json
|
||||
- apt-get install -y --no-install-recommends ca-certificates fuse-overlayfs buildah
|
||||
- echo "${CI_REGISTRY_PASSWORD}" | buildah login --username "${CI_REGISTRY_USER}" --password-stdin "${CI_REGISTRY}"
|
||||
- export IMAGE_TAG_1="${CI_REGISTRY}/${CI_PROJECT_PATH}/develop:not-for-production"
|
||||
- export IMAGE_TAG_2="${CI_REGISTRY}/${CI_PROJECT_PATH}/develop:not-for-production-$(date +%Y%m%d)"
|
||||
|
@ -205,7 +201,7 @@ build:container:
|
|||
|
||||
cargo:check:msrv:
|
||||
stage: test
|
||||
image: docker.io/rust:1.74-slim-bookworm
|
||||
image: docker.io/rust:1.74-bookworm
|
||||
rules:
|
||||
- if: $TEST == 'true'
|
||||
when: always
|
||||
|
@ -221,8 +217,7 @@ cargo:check:msrv:
|
|||
when: always
|
||||
services: []
|
||||
before_script:
|
||||
- apt-get update && apt-get -y upgrade
|
||||
- apt-get install -y --no-install-recommends build-essential clang mold python3 perl nodejs postgresql-client
|
||||
- apt-get update && apt-get install -y --no-install-recommends build-essential clang mold python3 perl nodejs postgresql-client
|
||||
- cp ci/cargo/config.toml /usr/local/cargo/config.toml
|
||||
- export CARGO_TARGET_DIR='ci/target-msrv'
|
||||
script:
|
||||
|
@ -264,8 +259,7 @@ cargo:clippy:
|
|||
when: always
|
||||
services: []
|
||||
before_script:
|
||||
- apt-get update && apt-get -y upgrade
|
||||
- apt-get install -y --no-install-recommends build-essential clang mold perl
|
||||
- apt-get update && apt-get install -y --no-install-recommends build-essential clang mold perl
|
||||
- cp ci/cargo/config.toml /usr/local/cargo/config.toml
|
||||
- rustup component add clippy
|
||||
script:
|
||||
|
@ -289,8 +283,7 @@ cargo:doc:
|
|||
when: always
|
||||
services: []
|
||||
before_script:
|
||||
- apt-get update
|
||||
- apt-get install -y --no-install-recommends build-essential clang mold nodejs npm
|
||||
- apt-get update && apt-get install -y --no-install-recommends build-essential clang mold nodejs npm
|
||||
- cp ci/cargo/config.toml /usr/local/cargo/config.toml
|
||||
script:
|
||||
- cargo doc --document-private-items
|
||||
|
@ -331,8 +324,7 @@ clean:
|
|||
- if: $CLEAN && $CI_PIPELINE_SOURCE == 'schedule'
|
||||
services: []
|
||||
before_script:
|
||||
- apt-get update && apt-get -y upgrade
|
||||
- apt-get -y --no-install-recommends install curl
|
||||
- apt-get update && apt-get -y --no-install-recommends install curl
|
||||
- curl -fsSL 'https://deb.nodesource.com/setup_18.x' | bash -
|
||||
- apt-get install -y --no-install-recommends nodejs
|
||||
- corepack enable
|
||||
|
|
296
Cargo.lock
generated
296
Cargo.lock
generated
|
@ -214,6 +214,7 @@ dependencies = [
|
|||
"cuid2",
|
||||
"emojis",
|
||||
"futures-util",
|
||||
"identicon-rs",
|
||||
"idna 1.0.2",
|
||||
"image",
|
||||
"isahc",
|
||||
|
@ -241,6 +242,7 @@ dependencies = [
|
|||
"tracing-subscriber",
|
||||
"url",
|
||||
"urlencoding",
|
||||
"uuid",
|
||||
"web-push",
|
||||
"zhconv",
|
||||
]
|
||||
|
@ -325,6 +327,12 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e0d60973d9320722cb1206f412740e162a33b8547ea8d6be75d7cff237c7a85"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -388,9 +396,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
|||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.16.1"
|
||||
version = "1.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e"
|
||||
checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
|
@ -406,9 +414,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
|||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.1"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
|
@ -418,9 +426,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.6"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
|
||||
checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
|
@ -611,6 +619,12 @@ version = "0.8.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.5.5"
|
||||
|
@ -684,9 +698,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "curl-sys"
|
||||
version = "0.4.73+curl-8.8.0"
|
||||
version = "0.4.74+curl-8.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "450ab250ecf17227c39afb9a2dd9261dc0035cb80f2612472fc0c4aac2dcb84d"
|
||||
checksum = "8af10b986114528fcdc4b63b6f5f021b7057618411046a4de2ba0f0149a097bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
@ -758,17 +772,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derivative"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
|
@ -846,6 +849,18 @@ dependencies = [
|
|||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "educe"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4bd92664bf78c4d3dba9b7cdafce6fa15b13ed3ed16175218196942e99168a8"
|
||||
dependencies = [
|
||||
"enum-ordinalize",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
|
@ -894,6 +909,26 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-ordinalize"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5"
|
||||
dependencies = [
|
||||
"enum-ordinalize-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-ordinalize-derive"
|
||||
version = "4.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
|
@ -927,6 +962,22 @@ version = "2.5.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.72.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"flume",
|
||||
"half",
|
||||
"lebe",
|
||||
"miniz_oxide",
|
||||
"rayon-core",
|
||||
"smallvec",
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
|
@ -963,9 +1014,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.30"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
|
||||
checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
|
@ -1165,6 +1216,16 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
|
@ -1290,7 +1351,7 @@ dependencies = [
|
|||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
"windows-core 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1420,6 +1481,17 @@ dependencies = [
|
|||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "identicon-rs"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05c4fdcf9f82fef33187cc52ad515de24786ec837837e69f641d1cb8157f4f02"
|
||||
dependencies = [
|
||||
"image",
|
||||
"sha3",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
|
@ -1451,11 +1523,14 @@ dependencies = [
|
|||
"bytemuck",
|
||||
"byteorder-lite",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"image-webp",
|
||||
"num-traits",
|
||||
"png",
|
||||
"qoi",
|
||||
"ravif",
|
||||
"rayon",
|
||||
"rgb",
|
||||
"tiff",
|
||||
"zune-core",
|
||||
|
@ -1480,9 +1555,9 @@ checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126"
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
|
@ -1663,6 +1738,12 @@ dependencies = [
|
|||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
|
@ -2439,9 +2520,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
|||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
|
@ -2514,6 +2598,15 @@ dependencies = [
|
|||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "2.0.1"
|
||||
|
@ -2631,9 +2724,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "0.26.0"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cc5b667390cb038bc65fc4b18c06e2550469f7e06a02d886f1a018a11f63563"
|
||||
checksum = "e902a69d09078829137b4a5d9d082e0490393537badd7c91a3d69d14639e115f"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
|
@ -2670,9 +2763,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.5"
|
||||
version = "1.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -2709,9 +2802,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.45"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade4539f42266ded9e755c605bdddf546242b2c961b03b06a7375260788a0523"
|
||||
checksum = "e12bc8d2f72df26a5d3178022df33720fbede0d31d82c7291662eff89836994d"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
@ -2906,9 +2999,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sea-orm"
|
||||
version = "0.12.15"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8814e37dc25de54398ee62228323657520b7f29713b8e238649385dbe473ee0"
|
||||
checksum = "f9d4ec1cdd8bdd3553d3c946079f58efa33fedc477f32603652652abcef96fe6"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
|
@ -2922,7 +3015,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"strum 0.25.0",
|
||||
"strum 0.26.3",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tracing",
|
||||
|
@ -2932,9 +3025,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sea-orm-macros"
|
||||
version = "0.12.15"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e115c6b078e013aa963cc2d38c196c2c40b05f03d0ac872fe06b6e0d5265603"
|
||||
checksum = "f363ead48b625a6f8f905322a820464f728fa4fe4f1c222bed5234ccf8fb8555"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
|
@ -2946,12 +3039,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sea-query"
|
||||
version = "0.30.7"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4166a1e072292d46dc91f31617c2a1cdaf55a8be4b5c9f4bf2ba248e3ac4999b"
|
||||
checksum = "7e5073b2cfed767511a57d18115f3b3d8bcb5690bf8c89518caec6cb22c0cd74"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"derivative",
|
||||
"educe",
|
||||
"inherent",
|
||||
"ordered-float",
|
||||
"serde_json",
|
||||
|
@ -2959,9 +3052,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sea-query-binder"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36bbb68df92e820e4d5aeb17b4acd5cc8b5d18b2c36a4dd6f4626aabfa7ab1b9"
|
||||
checksum = "754965d4aee6145bec25d0898e5c931e6c22859789ce62fd85a42a15ed5a8ce3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"sea-query",
|
||||
|
@ -3002,18 +3095,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.204"
|
||||
version = "1.0.205"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||
checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.204"
|
||||
version = "1.0.205"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -3022,11 +3115,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.120"
|
||||
version = "1.0.122"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
|
||||
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
@ -3454,9 +3548,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.25.0"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
|
@ -3524,15 +3618,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.30.13"
|
||||
version = "0.31.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3"
|
||||
checksum = "d4115055da5f572fff541dd0c4e61b0262977f453cc9fe04be83aba25a89bdab"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"memchr",
|
||||
"ntapi",
|
||||
"once_cell",
|
||||
"windows",
|
||||
]
|
||||
|
||||
|
@ -3551,18 +3644,19 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.15"
|
||||
version = "0.12.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2"
|
||||
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.10.1"
|
||||
version = "3.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
|
||||
checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand 2.1.0",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
@ -3753,9 +3847,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.16"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
|
@ -3765,18 +3859,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.7"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.17"
|
||||
version = "0.22.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16"
|
||||
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
|
@ -3947,6 +4041,8 @@ version = "1.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"rand",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -4074,8 +4170,9 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
|||
|
||||
[[package]]
|
||||
name = "web-push"
|
||||
version = "0.10.1"
|
||||
source = "git+https://github.com/pimeys/rust-web-push.git?rev=40febe4085e3cef9cdfd539c315e3e945aba0656#40febe4085e3cef9cdfd539c315e3e945aba0656"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49214685777c429ac44a92780983f4fadeecddd8c08c463d8196521e17e974bd"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.13.1",
|
||||
|
@ -4139,11 +4236,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.52.0"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-core 0.57.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
|
@ -4156,6 +4253,49 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
@ -4297,9 +4437,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.16"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c"
|
||||
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -4352,6 +4492,7 @@ version = "0.7.35"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
|
@ -4457,9 +4598,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.12+zstd.1.5.6"
|
||||
version = "2.0.13+zstd.1.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13"
|
||||
checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
|
@ -4471,6 +4612,15 @@ version = "0.4.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.4.13"
|
||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -24,6 +24,7 @@ convert_case = { version = "0.6.0", default-features = false }
|
|||
cuid2 = { version = "0.1.2", default-features = false }
|
||||
emojis = { version = "0.6.3", default-features = false }
|
||||
futures-util = { version = "0.3.30", default-features = false }
|
||||
identicon-rs = "5.0.1"
|
||||
idna = { version = "1.0.2", default-features = false }
|
||||
image = { version = "0.25.2", default-features = false }
|
||||
isahc = { version = "1.7.2", default-features = false }
|
||||
|
@ -33,15 +34,15 @@ pretty_assertions = { version = "1.4.0", default-features = false }
|
|||
proc-macro2 = { version = "1.0.86", default-features = false }
|
||||
quote = { version = "1.0.36", default-features = false }
|
||||
rand = { version = "0.8.5", default-features = false }
|
||||
redis = { version = "0.26.0", default-features = false }
|
||||
regex = { version = "1.10.5", default-features = false }
|
||||
redis = { version = "0.26.1", default-features = false }
|
||||
regex = { version = "1.10.6", default-features = false }
|
||||
rmp-serde = { version = "1.3.0", default-features = false }
|
||||
sea-orm = { version = "0.12.15", default-features = false }
|
||||
serde = { version = "1.0.204", default-features = false }
|
||||
serde_json = { version = "1.0.120", default-features = false }
|
||||
sea-orm = { version = "1.0.0", default-features = false }
|
||||
serde = { version = "1.0.205", default-features = false }
|
||||
serde_json = { version = "1.0.122", default-features = false }
|
||||
serde_yaml = { version = "0.9.34", default-features = false }
|
||||
syn = { version = "2.0.72", default-features = false }
|
||||
sysinfo = { version = "0.30.13", default-features = false }
|
||||
sysinfo = { version = "0.31.2", default-features = false }
|
||||
thiserror = { version = "1.0.63", default-features = false }
|
||||
tokio = { version = "1.39.2", default-features = false }
|
||||
tokio-test = { version = "0.4.4", default-features = false }
|
||||
|
@ -49,7 +50,8 @@ tracing = { version = "0.1.40", default-features = false }
|
|||
tracing-subscriber = { version = "0.3.18", default-features = false }
|
||||
url = { version = "2.5.2", default-features = false }
|
||||
urlencoding = { version = "2.1.3", default-features = false }
|
||||
web-push = { git = "https://github.com/pimeys/rust-web-push.git", rev = "40febe4085e3cef9cdfd539c315e3e945aba0656", default-features = false }
|
||||
uuid = "1.10.0"
|
||||
web-push = { version = "0.10.2", default-features = false }
|
||||
zhconv = "0.3.1"
|
||||
|
||||
# subdependencies
|
||||
|
|
|
@ -16,7 +16,6 @@ RUN corepack enable && corepack prepare pnpm@latest --activate
|
|||
# Build
|
||||
COPY . ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
RUN cargo fetch --locked --manifest-path Cargo.toml
|
||||
RUN NODE_ENV='production' NODE_OPTIONS='--max_old_space_size=3072' pnpm run build
|
||||
|
||||
# Trim down the dependencies to only those for production
|
||||
|
|
|
@ -2,7 +2,7 @@ version: "3"
|
|||
|
||||
services:
|
||||
web:
|
||||
image: docker.io/node:18.19.0-bookworm
|
||||
image: docker.io/node:18.20.0-bookworm
|
||||
container_name: firefish_web
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
|
|
|
@ -15,7 +15,7 @@ sudo apt install build-essential python3 curl wget git lsb-release
|
|||
|
||||
### Node.js
|
||||
|
||||
Firefish requires Node.js v18.19.0 or later. While you can choose any versions between v18.19.0 and the latest version (v22.2.0 as of writing), we recommend that you install v18.x so as not to use new features inadvertently and introduce incompatibility issues.
|
||||
Firefish requires Node.js v18.20.0 or later. While you can choose any versions between v18.20.0 and the latest version (v22.2.0 as of writing), we recommend that you install v18.x so as not to use new features inadvertently and introduce incompatibility issues.
|
||||
|
||||
Instructions can be found at [this repository](https://github.com/nodesource/distributions).
|
||||
|
||||
|
|
|
@ -7,6 +7,15 @@ This changelog is not an exhaustive list. Code refactorings, minor bug fixes, do
|
|||
- Server administrators must check [notice-for-admins.md](https://firefish.dev/firefish/firefish/-/blob/main/docs/notice-for-admins.md) as well.
|
||||
- Third-party client/bot developers may want to check [api-change.md](https://firefish.dev/firefish/firefish/-/blob/main/docs/api-change.md) as well.
|
||||
|
||||
## [v20240809](https://firefish.dev/firefish/firefish/-/merge_requests/11262/commits)
|
||||
|
||||
- Add writing mode (right-to-left, vertical) support (!11222)
|
||||
- Fix bugs
|
||||
|
||||
### Breaking change
|
||||
|
||||
The random icon generator has been changed, so your icon will be changed if you haven't set your icon image and random icon generation is enabled on your server.
|
||||
|
||||
## [v20240729](https://firefish.dev/firefish/firefish/-/merge_requests/11214/commits)
|
||||
|
||||
- Fix bugs (including a medium severity security issue)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
BEGIN;
|
||||
|
||||
DELETE FROM "migrations" WHERE name IN (
|
||||
'SetEmojiPublicUrl1722346019160',
|
||||
'SetAccessTokenName1722134626110',
|
||||
'CreateSystemActors1720618854585',
|
||||
'AddMastodonSubscriptionType1715181461692',
|
||||
|
@ -41,6 +42,9 @@ DELETE FROM "migrations" WHERE name IN (
|
|||
'RemoveNativeUtilsMigration1705877093218'
|
||||
);
|
||||
|
||||
-- set-emoji-public-url
|
||||
ALTER TABLE "emoji" ALTER COLUMN "publicUrl" SET DEFAULT '';
|
||||
|
||||
-- addMastodonSubscriptionType
|
||||
ALTER TABLE "sw_subscription" DROP COLUMN "subscriptionTypes";
|
||||
DROP TYPE "push_subscription_type";
|
||||
|
|
|
@ -8,7 +8,7 @@ Firefish depends on the following software.
|
|||
|
||||
### Runtime dependencies
|
||||
|
||||
- At least [NodeJS](https://nodejs.org/en/) v18.19.0 (v20/v22 recommended)
|
||||
- At least [NodeJS](https://nodejs.org/en/) v18.20.0 (v20/v22 recommended)
|
||||
- At least [PostgreSQL](https://www.postgresql.org/) v12 (v16 recommended) with [PGroonga](https://pgroonga.github.io/) extension
|
||||
- At least [Redis](https://redis.io/) v7 or [Valkey](https://valkey.io/) v7
|
||||
- Web Proxy (one of the following)
|
||||
|
|
|
@ -6,6 +6,14 @@ You can skip intermediate versions when upgrading from an old version, but pleas
|
|||
|
||||
Please take a look at #10947.
|
||||
|
||||
## v20240809
|
||||
|
||||
### For systemd/pm2 users
|
||||
|
||||
Required Node.js version has been bumped from v18.19.0 to v18.20.0.
|
||||
|
||||
As written in the [v20240710 note](https://firefish.dev/firefish/firefish/-/blob/7660050d9938a5a92293bb8acc361a0ef0715912/docs/notice-for-admins.md#v20240710), it is highly recommended that you use an even newer version since v18.20.0 has known vulnerabilities.
|
||||
|
||||
## v20240725
|
||||
|
||||
### For LibreTranslate self-hosters
|
||||
|
|
|
@ -735,7 +735,7 @@ _notification:
|
|||
pollEnded: Es resultat de la enquesta ja està disponible
|
||||
emptyPushNotificationMessage: Les notificacions push s'han actualitzat
|
||||
youWereInvitedToGroup: "{userName} t'ha convidat a un grup"
|
||||
reacted: Ha reaccionat a la teva publicació
|
||||
reacted: han reaccionat a la teva publicació
|
||||
renoted: Ha impulsat la teva publicació
|
||||
voted: Ha votat a la teva enquesta
|
||||
andCountUsers: I {count} usuaris més {acted}
|
||||
|
@ -978,7 +978,7 @@ recipient: Destinatari(s)
|
|||
annotation: Comentaris
|
||||
blockedInstances: Servidors bloquejats
|
||||
blockedInstancesDescription: Llista les adreces dels servidors que vols bloquejar.
|
||||
Els servidors de la llista no podrán comunicarse amb aquests servidors.
|
||||
Els servidors de la llista no podrán comunicarse amb aquest servidor.
|
||||
hiddenTags: Etiquetes amagades
|
||||
hiddenTagsDescription: 'Enumereu les etiquetes (sense el #) que voleu ocultar de tendències
|
||||
i explorar. Les etiquetes ocultes encara es poden descobrir per altres mitjans.'
|
||||
|
@ -1934,6 +1934,12 @@ _permissions:
|
|||
"read:reactions": Consulta les teves reaccions
|
||||
"read:pages": Consulta la teva pàgina
|
||||
"read:page-likes": Veure les pàgines que t'agraden
|
||||
push: Enviar notificacions emergents
|
||||
follow: Seguir i deixar de seguir comptes
|
||||
read: Llegir (llegir línies de temps, notificacions, reaccions, silenciats, informació
|
||||
dels comptes, etc.)
|
||||
write: Escriure (escriure publicacions, reaccionar a publicacions, silenciar usuaris,
|
||||
editar informació dels comptes, etc.)
|
||||
_poll:
|
||||
noOnlyOneChoice: Calen almenys dues opcions
|
||||
canMultipleVote: Permet seleccionar diverses opcions
|
||||
|
@ -2228,7 +2234,7 @@ useCdnDescription: Carrega alguns dels recursos estàtics com ara Twemoji des de
|
|||
releaseToReload: Deixa anar per actualitzar
|
||||
reloading: Actualitzant
|
||||
enableTimelineStreaming: Actualitza les línies de temps automàticament
|
||||
enablePullToRefresh: Activa "Baixa per actualitzar"
|
||||
enablePullToRefresh: Activa "Baixa per recarregar"
|
||||
pullDownToReload: Baixa per actualitzar
|
||||
pullToRefreshThreshold: Distancia de baixada per actualitzar
|
||||
searchWords: Paraules / ID o adreça URL que vols cercar
|
||||
|
@ -2327,3 +2333,10 @@ addAlt4MeTag: "Afegeix automàticament l'etiqueta #Alt4Me a les teves publicacio
|
|||
strongPassword: Bona contrasenya
|
||||
turnOffCatLanguage: Desactiva la conversió al llenguatge de gat
|
||||
announcement: Anunci
|
||||
_writingMode:
|
||||
horizontalTB: Horitzontal, de d'alt a baix
|
||||
verticalRL: Vertical, de dreta a esquerra, lateralment
|
||||
verticalLRUpright: Vertical, esquerra andreta, cap amunt
|
||||
verticalRLUpright: Vertical, de dreta i esquerra, cap amunt
|
||||
verticalLR: Vertical, d'esquerra a dreta, lateralment
|
||||
writingMode: Mode d'escriptura
|
||||
|
|
|
@ -243,7 +243,7 @@ clearCachedFiles: "Clear cache"
|
|||
clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote files?"
|
||||
blockedInstances: "Blocked Servers"
|
||||
blockedInstancesDescription: "List the hostnames of the servers that you want to block.
|
||||
Listed servers will no longer be able to communicate with this servers."
|
||||
Listed servers will no longer be able to communicate with this server."
|
||||
silencedInstances: "Silenced Servers"
|
||||
silencedInstancesDescription: "List the hostnames of the servers that you want to
|
||||
silence. Accounts in the listed servers are treated as \"Silenced\", can only make
|
||||
|
@ -1200,7 +1200,7 @@ privateDescription: "Make visible for you only"
|
|||
makePrivate: "Make private"
|
||||
makePrivateConfirm: "This operation will send a deletion request to remote servers
|
||||
and change the visibility to private. Proceed?"
|
||||
enablePullToRefresh: "Enable \"Pull down to refresh\""
|
||||
enablePullToRefresh: "Enable “Pull down to reload”"
|
||||
pullToRefreshThreshold: "Pull distance for reloading"
|
||||
pullDownToReload: "Pull down to reload"
|
||||
releaseToReload: "Release to reload"
|
||||
|
@ -1211,13 +1211,13 @@ searchWordsDescription: "Enter the search term here to search for posts. Separat
|
|||
words with a space for an AND search, or 'OR' (without quotes) between words for
|
||||
an OR search.\nFor example, 'morning night' will find posts that contain both 'morning'
|
||||
and 'night', and 'morning OR night' will find posts that contain either 'morning'
|
||||
or 'night' (or both).\nYou can also filter out certain word(s) from the search results, like
|
||||
'sleepy -morning -breakfast'. Moreover, you can combine these AND/OR/exclude conditions like
|
||||
'(morning OR night) sleepy -breakfast'.\nIf you want to search for a sequence of words (e.g., a sentence),
|
||||
you must put it in double quotes, not to make it an AND search: \"Today I learned\"\
|
||||
\n\nIf you want to go to a specific user page or post page, enter the ID or URL
|
||||
in this field and click the 'Lookup' button. Clicking 'Search' will search for posts
|
||||
that literally contain the ID/URL."
|
||||
or 'night' (or both).\nYou can also filter out certain word(s) from the search results,
|
||||
like 'sleepy -morning -breakfast'. Moreover, you can combine these AND/OR/exclude
|
||||
conditions like '(morning OR night) sleepy -breakfast'.\nIf you want to search for
|
||||
a sequence of words (e.g., a sentence), you must put it in double quotes, not to
|
||||
make it an AND search: \"Today I learned\"\n\nIf you want to go to a specific user
|
||||
page or post page, enter the ID or URL in this field and click the 'Lookup' button.
|
||||
Clicking 'Search' will search for posts that literally contain the ID/URL."
|
||||
searchUsers: "Posted by (optional)"
|
||||
searchUsersDescription: "To search for posts by a specific user/server, enter the
|
||||
ID (@user@example.com, or @user for a local user) or domain name (example.com).\n
|
||||
|
@ -1240,7 +1240,8 @@ noAltTextWarning: "Some attached file(s) have no description. Did you forget to
|
|||
showNoAltTextWarning: "Show a warning if you attempt to post files without a description"
|
||||
showAddFileDescriptionAtFirstPost: "Automatically open a form to write a description
|
||||
when you attempt to post files without a description"
|
||||
addAlt4MeTag: "Automatically append #Alt4Me hashtag to your post if attached file has no description"
|
||||
addAlt4MeTag: "Automatically append #Alt4Me hashtag to your post if attached file
|
||||
has no description"
|
||||
turnOffCatLanguage: "Turn off cat language conversion"
|
||||
|
||||
_emojiModPerm:
|
||||
|
@ -1356,6 +1357,13 @@ _nsfw:
|
|||
respect: "Hide NSFW media"
|
||||
ignore: "Don't hide NSFW media"
|
||||
force: "Hide all media"
|
||||
writingMode: "Writing mode"
|
||||
_writingMode:
|
||||
horizontalTB: "Horizontal, top to bottom"
|
||||
verticalLR: "Vertical, left to right, sideways"
|
||||
verticalRL: "Vertical, right to left, sideways"
|
||||
verticalLRUpright: "Vertical, left to right, upright"
|
||||
verticalRLUpright: "Vertical, right to left, upright"
|
||||
_mfm:
|
||||
play: "Play MFM"
|
||||
stop: "Stop MFM"
|
||||
|
@ -1668,8 +1676,10 @@ _2fa:
|
|||
removeKeyConfirm: "Really delete the {name} key?"
|
||||
token: "2FA Token"
|
||||
_permissions:
|
||||
read: "Read (read timelines, notifications, reactions, mutes, account information, etc.)"
|
||||
write: "Write (make posts, react to posts, mute users, edit account information, etc.)"
|
||||
read: "Read (read timelines, notifications, reactions, mutes, account information,
|
||||
etc.)"
|
||||
write: "Write (make posts, react to posts, mute users, edit account information,
|
||||
etc.)"
|
||||
push: "Send push notifications"
|
||||
follow: "Follow and unfollow accounts"
|
||||
"read:account": "View your account information"
|
||||
|
|
|
@ -9,3 +9,24 @@ notifications: Sciigoj
|
|||
username: Uzantnomo
|
||||
password: Pasvorto
|
||||
forgotPassword: Forgesa pasvorto
|
||||
fetchingAsApObject: Kaptante de Federujo
|
||||
ok: O kej
|
||||
noNotes: Ne afiŝoj
|
||||
noNotifications: Ne sciigiloj
|
||||
openInWindow: Malfermu en fenestro
|
||||
profile: Profilo
|
||||
gotIt: Jam estas!
|
||||
cancel: Mezfini
|
||||
noThankYou: Ne dankon
|
||||
renotedBy: Potenci de {user}
|
||||
noAccountDescription: Tiu ĉi uzanto eĉ ne skribis ilia biografio.
|
||||
enterUsername: Tajpu uzantnomon
|
||||
instance: Servilo
|
||||
settings: Agordoj
|
||||
basicSettings: Bazaj Agordoj
|
||||
otherSettings: Aliaj Agordoj
|
||||
login: Ensaluti
|
||||
loggingIn: Ensalutante
|
||||
logout: Elsaluti
|
||||
timeline: Templinio
|
||||
signup: Registru
|
||||
|
|
|
@ -1121,6 +1121,13 @@ _nsfw:
|
|||
respect: "閲覧注意のメディアは隠す"
|
||||
ignore: "閲覧注意のメディアを隠さない"
|
||||
force: "常にメディアを隠す"
|
||||
writingMode: "組み方向"
|
||||
_writingMode:
|
||||
horizontalTB: "横組み、上から下"
|
||||
verticalLR: "縦組み、左から右、英数字90度回転"
|
||||
verticalRL: "縦組み、右から左、英数字90度回転"
|
||||
verticalLRUpright: "縦組み、左から右、英数字1字ずつ配置"
|
||||
verticalRLUpright: "縦組み、右から左、英数字1字ずつ配置"
|
||||
_mfm:
|
||||
cheatSheet: "MFMチートシート"
|
||||
intro: "MFMは、MisskeyやFirefish、Akkomaなどの様々な場所で使用できるマークアップ言語です。ここでは、MFMで使用可能な構文一覧が確認できます。"
|
||||
|
@ -1425,6 +1432,10 @@ _permissions:
|
|||
"write:gallery": "ギャラリーを操作する"
|
||||
"read:gallery-likes": "ギャラリーのいいねを見る"
|
||||
"write:gallery-likes": "ギャラリーのいいねを操作する"
|
||||
read: 読み取り(タイムライン・通知・ミュート・アカウント情報などの読み込み)
|
||||
write: 書き込み(投稿・リアクション・ミュート・アカウント情報など)
|
||||
push: プッシュ通知の送信
|
||||
follow: アカウントのフォローとフォロー解除
|
||||
_auth:
|
||||
shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?"
|
||||
shareAccessAsk: "アカウントへのアクセスを許可しますか?"
|
||||
|
|
|
@ -139,9 +139,9 @@ settingGuide: "추천 설정"
|
|||
cacheRemoteFiles: "리모트 파일을 캐시"
|
||||
cacheRemoteFilesDescription: "이 설정을 해지하면 리모트 파일을 캐시하지 않고 해당 파일을 직접 링크하게 됩니다. 그에 따라
|
||||
서버의 저장 공간을 절약할 수 있지만, 썸네일이 생성되지 않기 때문에 통신량이 증가합니다."
|
||||
flagAsBot: "나는 봇입니다"
|
||||
flagAsBotDescription: "이 계정을 자동화된 수단으로 운용할 경우에 활성화해 주세요. 이 플래그를 활성화하면, 다른 봇이 이를 참고하여
|
||||
봇 끼리의 무한 연쇄 반응을 회피하거나, 이 계정의 시스템 상에서의 취급이 Bot 운영에 최적화되는 등의 변화가 생깁니다."
|
||||
flagAsBot: "이 계정은 자동화된 계정입니다"
|
||||
flagAsBotDescription: "이 계정을 자동화된 수단으로 운용할 경우에 활성화해 주세요. 이 플래그를 활성화하면, 다른 자동화 계정이
|
||||
이를 참고하여 자동화 계정 간 무한 연쇄 반응을 회피하거나, 이 계정의 시스템 상에서의 취급이 자동화 계정 운영에 최적화되는 등의 변화가 생깁니다."
|
||||
flagAsCat: "나는 고양이다냥"
|
||||
flagAsCatDescription: "고양이귀를 쓰고 냥냥거려요!"
|
||||
flagShowTimelineReplies: "타임라인에 게시물의 답글을 표시하기"
|
||||
|
@ -261,7 +261,7 @@ agreeTo: "{0}에 동의"
|
|||
tos: "이용 약관"
|
||||
start: "시작하기"
|
||||
home: "홈"
|
||||
remoteUserCaution: "리모트 유저이기 때문에, 정보가 정확하지 않을 수 있습니다."
|
||||
remoteUserCaution: "리모트 유저는 모든 정보가 표시되지 않습니다."
|
||||
activity: "활동"
|
||||
images: "이미지"
|
||||
birthday: "생일"
|
||||
|
@ -441,8 +441,8 @@ usernameInvalidFormat: "a~z, A~Z, 0-9, _를 사용할 수 있습니다."
|
|||
tooShort: "너무 짧습니다"
|
||||
tooLong: "너무 깁니다"
|
||||
weakPassword: "약한 비밀번호"
|
||||
normalPassword: "좋은 비밀번호"
|
||||
veryStrongPassword: "강한 비밀번호"
|
||||
normalPassword: "보통 비밀번호"
|
||||
veryStrongPassword: "매우 강한 비밀번호"
|
||||
passwordMatched: "일치합니다"
|
||||
passwordNotMatched: "일치하지 않습니다"
|
||||
signinWith: "{x}로 로그인"
|
||||
|
@ -605,7 +605,7 @@ emptyToDisableSmtpAuth: "SMTP 인증을 사용하지 않으려면 공란으로
|
|||
smtpSecure: "SMTP 연결에 Implicit SSL/TTS 사용"
|
||||
smtpSecureInfo: "STARTTLS 사용 시에는 해제합니다"
|
||||
testEmail: "이메일 전송 테스트"
|
||||
wordMute: "단어 뮤트"
|
||||
wordMute: "단어 및 언어 뮤트"
|
||||
regexpError: "정규 표현식 오류"
|
||||
regexpErrorDescription: "{tab}단어 뮤트 {line}행의 정규 표현식에 오류가 발생했습니다:"
|
||||
instanceMute: "서버 뮤트"
|
||||
|
@ -798,7 +798,7 @@ customCss: "CSS 사용자화"
|
|||
customCssWarn: "이 설정은 기능을 알고 있는 경우에만 사용해야 합니다. 잘못된 값을 입력하면 클라이언트가 정상적으로 작동하지 않을 수
|
||||
있습니다."
|
||||
global: "글로벌"
|
||||
squareAvatars: "프로필 아이콘을 사각형으로 표시"
|
||||
squareAvatars: "고양이가 아닌 프로필 아이콘을 사각형으로 표시"
|
||||
sent: "전송"
|
||||
received: "수신"
|
||||
searchResult: "검색 결과"
|
||||
|
@ -942,7 +942,8 @@ _accountDelete:
|
|||
inProgress: "삭제 진행 중"
|
||||
_ad:
|
||||
back: "뒤로"
|
||||
reduceFrequencyOfThisAd: "이 광고의 표시 빈도 낮추기"
|
||||
reduceFrequencyOfThisAd: "이 배너의 표시 빈도 낮추기"
|
||||
adsBy: '{by}에 의한 커뮤니티 배너'
|
||||
_forgotPassword:
|
||||
enterEmail: "여기에 계정에 등록한 메일 주소를 입력해 주세요. 입력한 메일 주소로 비밀번호 재설정 링크를 발송합니다."
|
||||
ifNoEmail: "메일 주소를 등록하지 않은 경우, 서버 관리자에게 문의해 주십시오."
|
||||
|
@ -1122,6 +1123,12 @@ _wordMute:
|
|||
soft: "보통"
|
||||
hard: "보다 높은 수준"
|
||||
mutedNotes: "뮤트된 게시물"
|
||||
mutePatterns: 뮤트할 패턴
|
||||
langDescription: 설정한 언어와 일치하는 게시물을 타임라인에서 숨깁니다.
|
||||
lang: 언어
|
||||
muteLangs: 뮤트할 언어
|
||||
muteLangsDescription: OR의 경우 공백 또는 줄바꿈으로 구분하십시오.
|
||||
muteLangsDescription2: en, fr, ja, zh 와 같은 언어 코드를 입력하세요.
|
||||
_instanceMute:
|
||||
instanceMuteDescription: "뮤트한 서버에서 오는 답글을 포함한 모든 게시물과 부스트를 뮤트합니다."
|
||||
instanceMuteDescription2: "한 줄에 하나씩 입력해 주세요"
|
||||
|
@ -1277,6 +1284,10 @@ _permissions:
|
|||
"write:gallery": "갤러리를 추가하거나 삭제합니다"
|
||||
"read:gallery-likes": "갤러리의 좋아요를 확인합니다"
|
||||
"write:gallery-likes": "갤러리에 좋아요를 추가하거나 취소합니다"
|
||||
push: 푸시 알림 보내기
|
||||
read: 읽기 (타임라인, 알림, 리액션, 뮤트, 계정 정보 등)
|
||||
write: 쓰기 (게시물 작성, 리액션 보내기, 뮤트하기, 계정 정보 수정 등)
|
||||
follow: 계정 팔로우 및 팔로우 해제
|
||||
_auth:
|
||||
shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?"
|
||||
shareAccessAsk: "이 애플리케이션이 계정에 접근하는 것을 허용하시겠습니까?"
|
||||
|
@ -1760,6 +1771,7 @@ _notification:
|
|||
reacted: 님의 리액션
|
||||
renoted: 님이 부스트
|
||||
voted: 님이 투표함
|
||||
andCountUsers: 와(과) {count}명이 {acted}
|
||||
_deck:
|
||||
alwaysShowMainColumn: "메인 칼럼 항상 표시"
|
||||
columnAlign: "칼럼 정렬"
|
||||
|
@ -1803,7 +1815,7 @@ privateMode: 비공개 모드
|
|||
audio: 오디오
|
||||
customKaTeXMacro: 커스텀 KaTeX 매크로
|
||||
replayTutorial: 튜토리얼 다시 보기
|
||||
renoteMute: 부스트 뮤트
|
||||
renoteMute: 타임라인에서 부스트 숨기기
|
||||
antennaInstancesDescription: 서버 호스트를 한 줄에 하나씩 입력하세요
|
||||
userSaysSomethingReason: '{name} 님이 {reason}에 대해 말했습니다'
|
||||
userSaysSomethingReasonQuote: '{name} 님이 {reason} 을 포함하는 게시물을 인용했습니다'
|
||||
|
@ -1874,11 +1886,11 @@ selectChannel: 채널 선택
|
|||
allowedInstancesDescription: 연합을 허가하려는 서버를 한 줄에 하나씩 입력합니다. 비공개 모드에서만 유효합니다.
|
||||
splash: 스플래시 화면
|
||||
preventAiLearningDescription: 업로드한 게시물이나 미디어를 AI 모델이 학습하지 말기를 요구합니다.
|
||||
isBot: 이 계정은 봇입니다
|
||||
isBot: 이 계정은 자동화된 계정입니다
|
||||
isAdmin: 관리자
|
||||
newer: 새로운 게시물
|
||||
older: 이전 게시물
|
||||
renoteUnmute: 부스트 뮤트 해제
|
||||
renoteUnmute: 타임라인에서 부스트 보이기
|
||||
accountMoved: '이 유저는 다른 계정으로 이사했습니다:'
|
||||
silencedInstances: 사일런스한 서버
|
||||
accessibility: 접근성
|
||||
|
@ -1917,8 +1929,7 @@ license: 라이선스
|
|||
migrationConfirm: "정말로 이 계정을 {account}로 이사하시겠습니까? 한 번 이사하면, 현재 이 계정은 두 번 다시 사용할 수
|
||||
없게 됩니다.\n또한, 이사 갈 계정에 현재 사용 중인 계정의 별칭을 올바르게 작성하였는지 다시 한 번 확인하십시오."
|
||||
noteId: 게시물 ID
|
||||
signupsDisabled: 현재 이 서버에서는 신규 등록을 받고 있지 않습니다. 초대 코드를 가지고 계신 경우 아래 칸에 입력해 주십시오. 초대
|
||||
코드를 가지고 있지 않더라도, 신규 등록이 열려 있는 다른 서버에 등록하실 수 있습니다!
|
||||
signupsDisabled: 현재 이 서버에서는 신규 등록을 받고 있지 않습니다. 초대 코드를 가지고 계신 경우 아래 칸에 입력해 주십시오.
|
||||
apps: 앱
|
||||
preventAiLearning: AI에 의한 학습을 막기
|
||||
isLocked: 이 계정은 팔로우를 수동으로 승인합니다
|
||||
|
@ -2002,3 +2013,108 @@ renotes: 부스트
|
|||
quotes: 인용
|
||||
sentFollowRequests: 팔로우 요청 보냄
|
||||
reactions: 리액션
|
||||
searchUsers: 작성한 사람 (옵션)
|
||||
replaceChatButtonWithAccountButton: 채팅 버튼을 계정 전환으로 변경
|
||||
searchEngine: 검색 MFM에서 사용할 검색 엔진
|
||||
turnOffCatLanguage: 냥체 변환을 끄기
|
||||
noSentFollowRequests: 승인 대기중인 팔로우 요청이 없습니다
|
||||
squareCatAvatars: 고양이 계정의 아이콘을 사각형으로 표시
|
||||
useThisAccountConfirm: 이 계정으로 계속 진행하시겠습니까?
|
||||
inputAccountId: '당신의 계정을 입력해 주세요 (예시: @firefish@info.firefish.dev)'
|
||||
pullToRefreshThreshold: 새로고침하기 위해 아래로 당길 길이
|
||||
pullDownToReload: 아래로 당겨 새로고침
|
||||
searchRange: 게시시각 범위 (옵션)
|
||||
showAddFileDescriptionAtFirstPost: 설명이 없는 첨부파일을 게시하려고 할 때에 자동으로 설명 작성창을 열기
|
||||
searchCwAndAlt: CW 주석과 미디어 설명에서도 검색하기
|
||||
publishTimelines: 비로그인 유저에게 타임라인을 공개
|
||||
addAlt4MeTag: '설명이 없는 파일을 게시할 때에 자동으로 #Alt4Me 해시태그를 포함하여 게시'
|
||||
_emojiModPerm:
|
||||
unauthorized: 없음
|
||||
full: 모두 허용
|
||||
add: 추가
|
||||
mod: 추가 및 편집
|
||||
openServerInfo: 게시물의 서버 이름을 클릭하여 서버 정보 열기
|
||||
iconSet: 아이콘 스타일
|
||||
showBigPostButton: 게시물 작성 폼에서 게시 버튼을 매우 크게 하기
|
||||
emojiModPerm: 커스텀 이모지 관리 권한
|
||||
releaseToReload: 놓아서 새로고침
|
||||
searchWords: 검색할 단어 / 조회할 ID 및 URL
|
||||
_later:
|
||||
secondsAgo: '{n}초 후'
|
||||
minutesAgo: '{n}분 후'
|
||||
hoursAgo: '{n}시간 후'
|
||||
daysAgo: '{n}일 후'
|
||||
weeksAgo: '{n}주 후'
|
||||
monthsAgo: '{n}개월 후'
|
||||
future: 미래
|
||||
justNow: 잠시 후
|
||||
yearsAgo: '{n}년 후'
|
||||
makePrivateConfirm: 리모트 서버에 삭제 요청을 보내고, 게시물의 공개 범위를 '비공개'로 전환합니다. 계속하시겠습니까?
|
||||
markLocalFilesNsfwByDefault: 모든 로컬 파일을 기본으로 NSFW로 처리
|
||||
markLocalFilesNsfwByDefaultDescription: 이 설정이 켜져 있더라도 유저는 직접 NSFW 설정을 해제할 수 있으며, 이
|
||||
설정은 기존 파일에는 적용되지 않습니다.
|
||||
showAttachedNotes: 이 파일이 첨부된 게시물 보기
|
||||
noLanguage: 언어 없음
|
||||
toReply: 답글
|
||||
toQuote: 인용
|
||||
replyMute: 타임라인에서 답글 숨기기
|
||||
searchPostsWithFiles: 첨부 파일이 있는 게시물만 검색
|
||||
antennaLimit: 각 유저가 생성 가능한 최대 안테나 수
|
||||
forMobile: 모바일
|
||||
reloading: 새로고침하는 중
|
||||
noAltTextWarning: 미디어 설명이 없는 파일이 있습니다. 깜빡하고 안 쓰셨나요?
|
||||
publishTimelinesDescription: 활성화하면 로그인하지 않아도 {url} 에서 로컬 타임라인과 글로벌 타임라인을 확인할 수 있게
|
||||
됩니다.
|
||||
cannotEditVisibility: 공개 범위를 변경할 수 없습니다
|
||||
private: 비공개
|
||||
preventMisclick: 클릭 실수 방지
|
||||
strongPassword: 강한 비밀번호
|
||||
_iconSets:
|
||||
light: 얇음
|
||||
regular: 보통
|
||||
bold: 굵음
|
||||
i18nServerInfo: 새 단말부터 {language}가 기본값으로 적용됩니다.
|
||||
privateDescription: 나를 제외한 모두에게 비공개
|
||||
enableTimelineStreaming: 타임라인을 자동으로 갱신
|
||||
toPost: 게시
|
||||
replyUnmute: 타임라인에서 답글 보이기
|
||||
attachedToNotes: 이 파일이 첨부된 게시물
|
||||
ipFirstAcknowledged: IP 주소를 처음 발견한 날짜
|
||||
driveCapacityOverride: 드라이브 용량 변경
|
||||
remoteFollow: 리모트 팔로우
|
||||
emojiModPermDescription: "추가: 커스텀 이모지의 신규 추가와 태그/카테고리/라이선스를 신규 지정할 수 있습니다.\n추가 및 편집:
|
||||
'추가'에 더해, 기존 이모지의 이름/카테고리/태그/라이선스를 변경할 수 있습니다.\n모두 허용: '추가 및 편집'에 더해, 기존 커스텀 이모지의
|
||||
삭제를 허가합니다.\n관리자 및 모더레이터는 이 설정을 무시하고 '모두 허용' 권한이 주어집니다."
|
||||
searchWordsDescription: "게시물을 검색하시려면, 여기에 검색어를 입력하여 주십시오. 공백으로 구분하여 AND, 'OR'으로 구분하여
|
||||
OR 검색이 가능합니다.\n예를 들어, '아침 저녁'이라고 검색할 경우 '아침'과 '저녁'을 동시에 포함하는 게시물을 검색하며, '아침 OR 저녁'이라고
|
||||
검색할 경우 '아침' 또는 '저녁' 둘 중 하나가 포함된 게시물을 검색합니다.\n-(대시) 부호를 붙여 특정 단어를 제외할 수 있으며, AND/OR/제외는
|
||||
괄호를 사용하여 동시에 사용할 수 있습니다. '(아침 OR 저녁) 졸려 -식곤증' 과 같이 사용합니다.\n띄어쓰기가 포함된 문자열을 검색하려는
|
||||
경우 \"오늘 뭐하지\"와 같이 큰따옴표를 사용하십시오.\n\n특정 유저나 게시물 페이지로 이동하시려면, 이 칸에 ID 또는 URL을 입력하고
|
||||
'조회'를 눌러 주십시오. '검색'을 누르면 해당 ID 또는 URL이 포함된 게시물을 검색합니다."
|
||||
searchUsersDescription: "특정 게시자가 올린 게시물을 검색할 경우, @user@example.com 혹은 (로컬 유저의 경우)
|
||||
@user와 같이 게시자의 ID를 입력하십시오. 도메인을 입력하여 해당 서버의 모든 유저가 작성한 게시물을 검색할 수 있습니다.\n\nme 를
|
||||
입력하면 자신의 게시물을 검색합니다. 자신의 검색 결과에는 미등재, 팔로워, 다이렉트, 비공개 게시물이 모두 포함됩니다.\n\nlocal 을 입력하면
|
||||
이 서버의 게시물을 검색합니다."
|
||||
searchRangeDescription: "게시 시각을 지정하실 경우, 20220615-20231031 과 같이 입력하십시오.\n\n올해 안에서
|
||||
검색하려면 연도를 생략할 수 있습니다. (0105-0106 또는 20231105-0110 과 같이)\n\n시작 날짜와 종료 날짜를 생략할 수 있습니다.
|
||||
예를 들어 -0102 의 경우 올해 1월 2일까지의 게시물을, 20231026- 의 경우 2023년 10월 26일 이후의 게시물을 검색합니다."
|
||||
showNoAltTextWarning: 설명이 없는 첨부 파일을 게시할 때에 경고
|
||||
vibrate: 진동 활성화
|
||||
clickToShowPatterns: 클릭하여 트랙을 표시
|
||||
announcement: 공지사항
|
||||
toEdit: 편집
|
||||
media: 미디어
|
||||
i18nServerChange: '{language}를 대신해서 사용합니다.'
|
||||
i18nServerSet: 새 기기의 표시 언어를 {language} 로 설정합니다.
|
||||
getQrCode: QR 코드 표시
|
||||
copyRemoteFollowUrl: 리모트 팔로우 URL 복사
|
||||
useCdn: 일부 구성요소를 CDN에서 가져오기
|
||||
useCdnDescription: Twemoji와 같은 일부 정적 구성요소를 JSDelivr CDN에서 가져옵니다. 비활성화할 경우 이 서버에서 가져옵니다.
|
||||
suggested: 추천
|
||||
showPreviewByDefault: 게시물 작성 화면에서 항상 미리보기 켜기
|
||||
hideFollowButtons: 잘못 누르기 쉬운 위치에 있는 팔로우 버튼을 숨기기
|
||||
replaceWidgetsButtonWithReloadButton: 위젯 버튼을 새로고침으로 변경
|
||||
postSearch: 이 서버의 게시물 검색
|
||||
makePrivate: 비공개로 전환
|
||||
enablePullToRefresh: "'아래로 당겨 새로고침'을 활성화"
|
||||
moderationNote: 모더레이션 노트
|
||||
|
|
|
@ -478,7 +478,7 @@ existingAccount: "现有的账号"
|
|||
regenerate: "重新生成"
|
||||
fontSize: "字体大小"
|
||||
noFollowRequests: "没有待批准的关注申请"
|
||||
noSentFollowRequests: "没有待回应的关注请求"
|
||||
noSentFollowRequests: "尚未发送任何关注请求"
|
||||
openImageInNewTab: "在新标签页中打开图片"
|
||||
dashboard: "管理面板"
|
||||
local: "本地"
|
||||
|
@ -1012,6 +1012,13 @@ _nsfw:
|
|||
respect: "隐藏敏感内容"
|
||||
ignore: "不隐藏敏感内容"
|
||||
force: "总是隐藏内容"
|
||||
writingMode: "书写方向"
|
||||
_writingMode:
|
||||
horizontalTB: "横排,由上向下"
|
||||
verticalLR: "直排,由左向右,字母旋转90°"
|
||||
verticalRL: "直排,由右向左,字母旋转90°"
|
||||
verticalLRUpright: "直排,由左向右,字母逐个排列"
|
||||
verticalRLUpright: "直排,由右向左,字母逐个排列"
|
||||
_mfm:
|
||||
cheatSheet: "MFM 代码速查表"
|
||||
intro: "MFM 是一种在 Misskey、Firefish、Akkoma 中使用的标记语言,可以在很多地方使用。您可以在此处查看所有可用的 MFM 语法的列表。"
|
||||
|
@ -1322,6 +1329,10 @@ _permissions:
|
|||
"write:gallery": "编辑图库"
|
||||
"read:gallery-likes": "读取喜欢的图片"
|
||||
"write:gallery-likes": "编辑喜欢的图片"
|
||||
read: 读取(时间线、通知、回应、静音、账户信息等)
|
||||
write: 修改(发表帖子、回应帖子、静音用户、编辑用户信息等)
|
||||
push: 发送推送通知
|
||||
follow: 关注和取消关注账号
|
||||
_auth:
|
||||
shareAccess: "您要授权允许 \"{name}\" 访问您的账号吗?"
|
||||
shareAccessAsk: "您确定要授权此应用访问您的账号吗?"
|
||||
|
@ -1990,7 +2001,7 @@ origin: 起源
|
|||
confirm: 确认
|
||||
importZip: 导入 ZIP
|
||||
exportZip: 导出 ZIP
|
||||
getQrCode: "获取二维码"
|
||||
getQrCode: "显示二维码"
|
||||
remoteFollow: "远程关注"
|
||||
copyRemoteFollowUrl: "复制远程关注 URL"
|
||||
objectStorageS3ForcePathStyleDesc: 打开此选项可构建格式为 "s3.amazonaws.com/<bucket>/" 而非 "<bucket>.s3.amazonaws.com"
|
||||
|
@ -2027,7 +2038,7 @@ _emojiModPerm:
|
|||
unauthorized: 未授权
|
||||
full: 全部允许
|
||||
moreUrls: 置顶页面
|
||||
enablePullToRefresh: 启用 “下拉刷新”
|
||||
enablePullToRefresh: 启用“下拉刷新”
|
||||
pullToRefreshThreshold: 触发下拉刷新所需的距离
|
||||
pullDownToReload: 下拉刷新
|
||||
releaseToReload: 释放刷新
|
||||
|
@ -2064,10 +2075,10 @@ searchCwAndAlt: 包括内容警告和文件描述
|
|||
publishTimelines: 为访客发布时间线
|
||||
publishTimelinesDescription: 如果启用,在用户登出时本地和全局时间线也会显示在 {url} 上。
|
||||
searchWordsDescription: "在此处输入搜索词以搜索帖子。交集搜索关键词之间使用空格进行区分,并集搜索关键词之间使用 OR 进行区分。\n例如
|
||||
'早上 晚上' 将查找包含 '早上' 和 '晚上' 的帖子,而 '早上 OR 晚上' 将查找包含 '早上' 或 '晚上' (以及同时包含两者)的帖子。\n您还可以组合交集/并集条件,例如
|
||||
'(早上 OR 晚上) 困了' 。\n如果您想搜索单词序列(例如一个英语句子),您必须将其放在双引号中,例如 \"Today I learned\" 以区分于交集搜索。\n
|
||||
\n如果您想转到特定的用户页面或帖子页面,请在此字段中输入用户 ID 或 URL,然后单击 “查询” 按钮。 单击 “搜索” 将搜索字面包含用户 ID/URL
|
||||
的帖子。"
|
||||
'早上 晚上' 将查找包含 '早上' 和 '晚上' 的帖子,而 '早上 OR 晚上' 将查找包含 '早上' 或 '晚上' (以及同时包含两者)的帖子。\n您也可以从搜索结果中过滤部分关键词,例如
|
||||
'困了 -早上 -早餐'。此外,您还可以组合交集/并集条件,例如 '(早上 OR 晚上) 困了 -早餐' 。\n如果您想搜索单词序列(例如一个英语句子),您必须将其放在双引号中,例如
|
||||
\"Today I learned\" 以区分于交集搜索。\n \n如果您想转到特定的用户页面或帖子页面,请在此字段中输入用户 ID 或 URL,然后单击 “查询”
|
||||
按钮。 单击 “搜索” 将搜索字面包含用户 ID/URL 的帖子。"
|
||||
searchRangeDescription: "如果您要过滤时间段,请按以下格式输入:20220615-20231031\n\n如果您省略年份(例如 0105-0106
|
||||
或 20231105-0110),它将被解释为当前年份。\n\n您还可以省略开始日期或结束日期。 例如 -0102 将过滤搜索结果以仅显示今年 1 月 2 日之前发布的帖子,而
|
||||
20231026- 将过滤结果以仅显示 2023 年 10 月 26 日之后发布的帖子。"
|
||||
|
@ -2076,7 +2087,7 @@ noAltTextWarning: 有些附件没有描述。您是否忘记写描述了?
|
|||
showNoAltTextWarning: 当您尝试发布没有描述的帖子附件时显示警告
|
||||
showAddFileDescriptionAtFirstPost: 当您首次尝试发布没有描述的帖子附件时自动弹出添加描述页面
|
||||
autocorrectNoteLanguage: 当帖子语言不符合自动检测的结果的时候显示警告
|
||||
incorrectLanguageWarning: "看上去您帖子使用的语言是{detected},但您选择的语言是{current}。\n要改为以{detected}发帖吗?"
|
||||
incorrectLanguageWarning: "检测到帖子语言可能是{detected}。\n要将发帖语言从{current}更改为{detected}吗?"
|
||||
noteEditHistory: "帖子编辑历史"
|
||||
media: 媒体
|
||||
slashQuote: "斜杠引用"
|
||||
|
@ -2087,3 +2098,6 @@ scheduledPostAt: "帖子将于 {time} 发送"
|
|||
scheduledDate: "发送日期"
|
||||
mergeThreadInTimeline: "将时间线内的连续回复合并成一串"
|
||||
mergeRenotesInTimeline: "合并同一个帖子的转发"
|
||||
turnOffCatLanguage: 关闭猫语转换
|
||||
strongPassword: 密码强度:良好
|
||||
addAlt4MeTag: '当附件没有描述时,自动在帖子中添加 #Alt4Me 标签'
|
||||
|
|
|
@ -1004,6 +1004,13 @@ _nsfw:
|
|||
respect: "隱藏敏感內容"
|
||||
ignore: "不隱藏敏感內容"
|
||||
force: "隱藏所有內容"
|
||||
writingMode: "書寫方向"
|
||||
_writingMode:
|
||||
horizontalTB: "橫排,由上而下"
|
||||
verticalLR: "直排,由左而右,字母旋轉90°"
|
||||
verticalRL: "直排,由右而左,字母旋轉90°"
|
||||
verticalLRUpright: "直排,由左而右,字母逐個排列"
|
||||
verticalRLUpright: "直排,由右而左,字母逐個排列"
|
||||
_mfm:
|
||||
cheatSheet: "MFM代碼小抄"
|
||||
intro: "MFM是Misskey、Firefish、Akkoma等專用的標記語言,可以在各個位置使用。 您可以這裏看到MFM可用語法列表。"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"name": "firefish",
|
||||
"version": "20240729",
|
||||
"version": "20240809",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://firefish.dev/firefish/firefish.git"
|
||||
},
|
||||
"packageManager": "pnpm@9.6.0",
|
||||
"packageManager": "pnpm@9.7.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"rebuild": "pnpm run clean && pnpm run build",
|
||||
|
@ -47,8 +47,8 @@
|
|||
"@biomejs/cli-darwin-x64": "1.8.3",
|
||||
"@biomejs/cli-linux-arm64": "1.8.3",
|
||||
"@biomejs/cli-linux-x64": "1.8.3",
|
||||
"@types/node": "20.14.13",
|
||||
"@types/node": "20.14.14",
|
||||
"execa": "9.3.0",
|
||||
"pnpm": "9.6.0"
|
||||
"pnpm": "9.7.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ chrono = { workspace = true }
|
|||
cuid2 = { workspace = true }
|
||||
emojis = { workspace = true }
|
||||
futures-util = { workspace = true, features = ["io"] }
|
||||
identicon-rs = { workspace = true }
|
||||
idna = { workspace = true, features = ["std", "compiled_data"] }
|
||||
image = { workspace = true, features = ["avif", "bmp", "gif", "ico", "jpeg", "png", "tiff", "webp"] }
|
||||
isahc = { workspace = true, features = ["http2", "text-decoding", "json"] }
|
||||
|
@ -42,13 +43,14 @@ sea-orm = { workspace = true, features = ["macros", "runtime-tokio-rustls", "sql
|
|||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
serde_yaml = { workspace = true }
|
||||
sysinfo = { workspace = true }
|
||||
sysinfo = { workspace = true, features = ["system", "disk"] }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["fs", "io-std", "io-util", "macros", "process", "rt-multi-thread", "signal", "sync", "time"] }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, features = ["ansi"] }
|
||||
url = { workspace = true }
|
||||
urlencoding = { workspace = true }
|
||||
uuid = { workspace = true, features = ["v4", "fast-rng"] }
|
||||
web-push = { workspace = true, features = ["isahc-client"] }
|
||||
zhconv = { workspace = true }
|
||||
|
||||
|
|
106
packages/backend-rs/index.d.ts
vendored
106
packages/backend-rs/index.d.ts
vendored
|
@ -48,8 +48,6 @@ export interface Acct {
|
|||
|
||||
export declare function acctToString(acct: Acct): string
|
||||
|
||||
export type Activity = 'Follow';
|
||||
|
||||
export interface Ad {
|
||||
id: string
|
||||
createdAt: DateTimeWithTimeZone
|
||||
|
@ -106,6 +104,57 @@ export type AntennaSrc = 'all'|
|
|||
'list'|
|
||||
'users';
|
||||
|
||||
export interface ApAccept {
|
||||
id: string
|
||||
type: ApObject
|
||||
actor: string
|
||||
object: ApFollow
|
||||
}
|
||||
|
||||
export interface ApEmoji {
|
||||
id: string
|
||||
type: ApObject
|
||||
name: string
|
||||
updated: string
|
||||
icon: Icon
|
||||
}
|
||||
|
||||
export interface ApFlag {
|
||||
type: ApObject
|
||||
actor: string
|
||||
content: string
|
||||
object: string
|
||||
}
|
||||
|
||||
export interface ApFollow {
|
||||
id: string
|
||||
type: ApObject
|
||||
actor: string
|
||||
object: string
|
||||
}
|
||||
|
||||
export interface ApHashtag {
|
||||
id: string
|
||||
type: ApObject
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface ApMention {
|
||||
type: ApObject
|
||||
href: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export type ApObject = 'Accept'|
|
||||
'Emoji'|
|
||||
'Flag'|
|
||||
'Follow'|
|
||||
'Hashtag'|
|
||||
'Mention'|
|
||||
'Image'|
|
||||
'Read'|
|
||||
'Tombstone';
|
||||
|
||||
export interface App {
|
||||
id: string
|
||||
createdAt: DateTimeWithTimeZone
|
||||
|
@ -117,6 +166,17 @@ export interface App {
|
|||
callbackUrl: string | null
|
||||
}
|
||||
|
||||
export interface ApRead {
|
||||
type: ApObject
|
||||
actor: string
|
||||
object: string
|
||||
}
|
||||
|
||||
export interface ApTombstone {
|
||||
id: string
|
||||
type: ApObject
|
||||
}
|
||||
|
||||
export interface AttestationChallenge {
|
||||
id: string
|
||||
userId: string
|
||||
|
@ -425,13 +485,6 @@ export interface Following {
|
|||
followeeSharedInbox: string | null
|
||||
}
|
||||
|
||||
export interface FollowRelay {
|
||||
id: string
|
||||
type: Activity
|
||||
actor: string
|
||||
object: string
|
||||
}
|
||||
|
||||
export interface FollowRequest {
|
||||
id: string
|
||||
createdAt: DateTimeWithTimeZone
|
||||
|
@ -487,6 +540,8 @@ export declare function genId(): string
|
|||
/** Generate an ID using a specific datetime */
|
||||
export declare function genIdAt(date: Date): string
|
||||
|
||||
export declare function genIdenticon(id: string): Promise<Buffer>
|
||||
|
||||
export declare function getFullApAccount(username: string, host?: string | undefined | null): string
|
||||
|
||||
export declare function getImageSizeFromUrl(url: string): Promise<ImageSize>
|
||||
|
@ -522,6 +577,12 @@ export interface Hashtag {
|
|||
attachedRemoteUsersCount: number
|
||||
}
|
||||
|
||||
export interface Icon {
|
||||
type: ApObject
|
||||
mediaType: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export interface IdConfig {
|
||||
length?: number
|
||||
fingerprint?: string
|
||||
|
@ -1265,7 +1326,23 @@ export type RelayStatus = 'accepted'|
|
|||
/** Delete all entries in the [attestation_challenge] table created at more than 5 minutes ago */
|
||||
export declare function removeOldAttestationChallenges(): Promise<void>
|
||||
|
||||
export declare function renderFollowRelay(relayId: string): Promise<FollowRelay>
|
||||
export declare function renderAccept(userId: string, followObject: ApFollow): ApAccept
|
||||
|
||||
export declare function renderEmoji(emoji: Emoji): ApEmoji
|
||||
|
||||
export declare function renderFlag(targetUserUri: string, comment: string): Promise<ApFlag>
|
||||
|
||||
export declare function renderFollow(follower: UserLike, followee: UserLike, requestId?: string | undefined | null): ApFollow
|
||||
|
||||
export declare function renderFollowRelay(relayId: string): Promise<ApFollow>
|
||||
|
||||
export declare function renderHashtag(tagName: string): ApHashtag
|
||||
|
||||
export declare function renderMention(user: UserLike): ApMention
|
||||
|
||||
export declare function renderRead(userId: string, messageUri: string): ApRead
|
||||
|
||||
export declare function renderTombstone(noteId: string): ApTombstone
|
||||
|
||||
export interface RenoteMuting {
|
||||
id: string
|
||||
|
@ -1423,8 +1500,6 @@ export declare function updateAntennasOnNewNote(note: Note, noteAuthor: Acct, no
|
|||
|
||||
export declare function updateMetaCache(): Promise<void>
|
||||
|
||||
export declare function updateNodeinfoCache(): Promise<void>
|
||||
|
||||
/** Usage statistics for this server. */
|
||||
export interface Usage {
|
||||
users: Users
|
||||
|
@ -1537,6 +1612,13 @@ export interface UserKeypair {
|
|||
privateKey: string
|
||||
}
|
||||
|
||||
export interface UserLike {
|
||||
id: string
|
||||
username: string
|
||||
host: string | null
|
||||
uri: string | null
|
||||
}
|
||||
|
||||
export interface UserList {
|
||||
id: string
|
||||
createdAt: DateTimeWithTimeZone
|
||||
|
|
|
@ -362,8 +362,8 @@ if (!nativeBinding) {
|
|||
}
|
||||
|
||||
module.exports.acctToString = nativeBinding.acctToString
|
||||
module.exports.Activity = nativeBinding.Activity
|
||||
module.exports.AntennaSrc = nativeBinding.AntennaSrc
|
||||
module.exports.ApObject = nativeBinding.ApObject
|
||||
module.exports.ChatEvent = nativeBinding.ChatEvent
|
||||
module.exports.ChatIndexEvent = nativeBinding.ChatIndexEvent
|
||||
module.exports.checkWordMute = nativeBinding.checkWordMute
|
||||
|
@ -384,6 +384,7 @@ module.exports.generateSecureRandomString = nativeBinding.generateSecureRandomSt
|
|||
module.exports.generateUserToken = nativeBinding.generateUserToken
|
||||
module.exports.genId = nativeBinding.genId
|
||||
module.exports.genIdAt = nativeBinding.genIdAt
|
||||
module.exports.genIdenticon = nativeBinding.genIdenticon
|
||||
module.exports.getFullApAccount = nativeBinding.getFullApAccount
|
||||
module.exports.getImageSizeFromUrl = nativeBinding.getImageSizeFromUrl
|
||||
module.exports.getInstanceActor = nativeBinding.getInstanceActor
|
||||
|
@ -437,7 +438,15 @@ module.exports.PushNotificationKind = nativeBinding.PushNotificationKind
|
|||
module.exports.PushSubscriptionType = nativeBinding.PushSubscriptionType
|
||||
module.exports.RelayStatus = nativeBinding.RelayStatus
|
||||
module.exports.removeOldAttestationChallenges = nativeBinding.removeOldAttestationChallenges
|
||||
module.exports.renderAccept = nativeBinding.renderAccept
|
||||
module.exports.renderEmoji = nativeBinding.renderEmoji
|
||||
module.exports.renderFlag = nativeBinding.renderFlag
|
||||
module.exports.renderFollow = nativeBinding.renderFollow
|
||||
module.exports.renderFollowRelay = nativeBinding.renderFollowRelay
|
||||
module.exports.renderHashtag = nativeBinding.renderHashtag
|
||||
module.exports.renderMention = nativeBinding.renderMention
|
||||
module.exports.renderRead = nativeBinding.renderRead
|
||||
module.exports.renderTombstone = nativeBinding.renderTombstone
|
||||
module.exports.safeForSql = nativeBinding.safeForSql
|
||||
module.exports.sendPushNotification = nativeBinding.sendPushNotification
|
||||
module.exports.shouldNyaify = nativeBinding.shouldNyaify
|
||||
|
@ -453,7 +462,6 @@ module.exports.unwatchNote = nativeBinding.unwatchNote
|
|||
module.exports.updateAntennaCache = nativeBinding.updateAntennaCache
|
||||
module.exports.updateAntennasOnNewNote = nativeBinding.updateAntennasOnNewNote
|
||||
module.exports.updateMetaCache = nativeBinding.updateMetaCache
|
||||
module.exports.updateNodeinfoCache = nativeBinding.updateNodeinfoCache
|
||||
module.exports.UserEmojiModPerm = nativeBinding.UserEmojiModPerm
|
||||
module.exports.UserEvent = nativeBinding.UserEvent
|
||||
module.exports.UserProfileFfvisibility = nativeBinding.UserProfileFfvisibility
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
"@napi-rs/cli": "3.0.0-alpha.62"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "napi build --features napi --no-const-enum --platform --release --output-dir ./built/",
|
||||
"fetch": "cargo fetch --locked --manifest-path ../../Cargo.toml",
|
||||
"build": "pnpm run fetch && napi build --features napi --no-const-enum --platform --release --output-dir ./built/ -- --frozen",
|
||||
"build:debug": "napi build --features napi --no-const-enum --platform --output-dir ./built/ --dts-header '/* auto-generated by NAPI-RS */\n/* Do NOT edit this file manually */\n\ntype DateTimeWithTimeZone = Date;\n\ntype Json = any;\n\n'"
|
||||
}
|
||||
}
|
||||
|
|
199
packages/backend-rs/src/cache/bare.rs
vendored
Normal file
199
packages/backend-rs/src/cache/bare.rs
vendored
Normal file
|
@ -0,0 +1,199 @@
|
|||
//! In-memory cache handler
|
||||
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use std::sync::Mutex;
|
||||
|
||||
/// Cache stored directly in memory
|
||||
pub struct Cache<T: Clone> {
|
||||
cache: Mutex<TimedData<T>>,
|
||||
ttl: Option<Duration>,
|
||||
}
|
||||
|
||||
struct TimedData<T: Clone> {
|
||||
value: Option<T>,
|
||||
last_updated: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl<T: Clone> Default for Cache<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Cache<T> {
|
||||
/// Creates a new cache object with no auto invalidation.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
cache: Mutex::new(TimedData {
|
||||
value: None,
|
||||
last_updated: DateTime::UNIX_EPOCH,
|
||||
}),
|
||||
ttl: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new cache object whose content is invalidated
|
||||
/// in the specified duration.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use backend_rs::cache::Cache;
|
||||
/// use chrono::Duration;
|
||||
/// static CACHE: Cache<i32> = Cache::new_with_ttl(Duration::seconds(1));
|
||||
///
|
||||
/// fn use_cache() {
|
||||
/// let data = 998244353;
|
||||
///
|
||||
/// // Set cache
|
||||
/// CACHE.set(data);
|
||||
///
|
||||
/// // wait for the cache to expire
|
||||
/// std::thread::sleep(std::time::Duration::from_millis(1100));
|
||||
///
|
||||
/// // Get cache
|
||||
/// let cache = CACHE.get();
|
||||
///
|
||||
/// assert!(cache.is_none());
|
||||
/// }
|
||||
/// ```
|
||||
pub const fn new_with_ttl(ttl: Duration) -> Self {
|
||||
Self {
|
||||
cache: Mutex::new(TimedData {
|
||||
value: None,
|
||||
last_updated: DateTime::UNIX_EPOCH,
|
||||
}),
|
||||
ttl: Some(ttl),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a cache. This function overwrites the existing data.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use backend_rs::cache::Cache;
|
||||
/// static CACHE: Cache<i32> = Cache::new();
|
||||
///
|
||||
/// fn use_cache() {
|
||||
/// let data = 998244353;
|
||||
///
|
||||
/// // Set cache
|
||||
/// CACHE.set(data);
|
||||
///
|
||||
/// // Get cache
|
||||
/// let cache = CACHE.get();
|
||||
///
|
||||
/// if let Some(cached_data) = cache {
|
||||
/// println!("found a cached value");
|
||||
/// assert_eq!(data, cached_data)
|
||||
/// } else {
|
||||
/// println!("cache not found");
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn set(&self, value: T) {
|
||||
if self.ttl.is_none() {
|
||||
let mut cache = match self.cache.lock() {
|
||||
Ok(cache) => cache,
|
||||
Err(err) => err.into_inner(),
|
||||
};
|
||||
cache.value = Some(value);
|
||||
} else {
|
||||
let mut cache = match self.cache.lock() {
|
||||
Ok(cache) => cache,
|
||||
Err(err) => err.into_inner(),
|
||||
};
|
||||
|
||||
*cache = TimedData {
|
||||
value: Some(value),
|
||||
last_updated: Utc::now(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a cache. Returns [`None`] is the cache is not set or expired.
|
||||
pub fn get(&self) -> Option<T> {
|
||||
let data = self.cache.lock().ok()?;
|
||||
|
||||
if let Some(ttl) = self.ttl {
|
||||
if data.last_updated + ttl < Utc::now() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
data.value.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::Cache;
|
||||
use chrono::Duration;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct Data {
|
||||
id: u64,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_and_get() {
|
||||
static CACHE: Cache<Data> = Cache::new();
|
||||
static CACHE_WITH_TTL: Cache<Data> = Cache::new_with_ttl(Duration::seconds(1));
|
||||
|
||||
let data = Data {
|
||||
id: 16,
|
||||
name: "Firefish".to_owned(),
|
||||
};
|
||||
|
||||
assert!(CACHE.get().is_none());
|
||||
assert!(CACHE_WITH_TTL.get().is_none());
|
||||
|
||||
CACHE.set(data.clone());
|
||||
assert_eq!(data, CACHE.get().unwrap());
|
||||
|
||||
CACHE_WITH_TTL.set(data.clone());
|
||||
assert_eq!(data, CACHE_WITH_TTL.get().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expire() {
|
||||
static CACHE: Cache<Data> = Cache::new_with_ttl(Duration::seconds(1));
|
||||
|
||||
let data = Data {
|
||||
id: 16,
|
||||
name: "Firefish".to_owned(),
|
||||
};
|
||||
|
||||
CACHE.set(data);
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(1100));
|
||||
|
||||
assert!(CACHE.get().is_none());
|
||||
}
|
||||
|
||||
static GLOBAL_CACHE_1: Cache<Data> = Cache::new();
|
||||
static GLOBAL_CACHE_2: Cache<Data> = Cache::new_with_ttl(Duration::milliseconds(2));
|
||||
|
||||
#[tokio::test]
|
||||
async fn use_cache_in_parallel() {
|
||||
let mut tasks = Vec::new();
|
||||
|
||||
async fn f() -> Data {
|
||||
Data {
|
||||
id: rand::random(),
|
||||
name: cuid2::create_id(),
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..20 {
|
||||
tasks.push(tokio::spawn(async {
|
||||
GLOBAL_CACHE_1.set(f().await);
|
||||
GLOBAL_CACHE_2.set(f().await);
|
||||
(GLOBAL_CACHE_1.get(), GLOBAL_CACHE_2.get())
|
||||
}))
|
||||
}
|
||||
for task in tasks {
|
||||
task.await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
7
packages/backend-rs/src/cache/mod.rs
vendored
Normal file
7
packages/backend-rs/src/cache/mod.rs
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
//! Cache handlers
|
||||
|
||||
pub mod bare;
|
||||
pub mod redis;
|
||||
|
||||
pub use bare::Cache;
|
||||
pub use redis::{delete, delete_all, delete_one, get, get_one, set, set_one, Category};
|
|
@ -1,6 +1,7 @@
|
|||
//! Utilities for using Redis cache
|
||||
|
||||
use crate::database::{redis_conn, redis_key, RedisConnError};
|
||||
use chrono::Duration;
|
||||
use redis::{AsyncCommands, RedisError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -10,6 +11,7 @@ pub enum Category {
|
|||
Block,
|
||||
Follow,
|
||||
CatLang,
|
||||
RandomIcon,
|
||||
#[cfg(test)]
|
||||
Test,
|
||||
}
|
||||
|
@ -22,6 +24,8 @@ pub enum Error {
|
|||
RedisConn(#[from] RedisConnError),
|
||||
#[error("failed to encode data for Redis")]
|
||||
Encode(#[from] rmp_serde::encode::Error),
|
||||
#[error("invalid cache TTL")]
|
||||
TTL,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -35,6 +39,7 @@ fn categorize(category: Category, key: &str) -> String {
|
|||
Category::Block => "blocking",
|
||||
Category::Follow => "following",
|
||||
Category::CatLang => "catlang",
|
||||
Category::RandomIcon => "randomIcon",
|
||||
#[cfg(test)]
|
||||
Category::Test => "usedOnlyForTesting",
|
||||
};
|
||||
|
@ -54,18 +59,19 @@ fn wildcard(category: Category) -> String {
|
|||
///
|
||||
/// * `key` : key (prefixed automatically)
|
||||
/// * `value` : (de)serializable value
|
||||
/// * `expire_seconds` : TTL
|
||||
/// * `ttl` : cache lifetime
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use backend_rs::database::cache;
|
||||
/// # use backend_rs::cache;
|
||||
/// use chrono::Duration;
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let key = "apple";
|
||||
/// let data = "I want to cache this string".to_owned();
|
||||
///
|
||||
/// // caches the data for 10 seconds
|
||||
/// cache::set(key, &data, 10).await?;
|
||||
/// cache::set(key, &data, Duration::seconds(10)).await?;
|
||||
///
|
||||
/// // get the cache
|
||||
/// let cached_data = cache::get::<String>(key).await?;
|
||||
|
@ -78,17 +84,16 @@ fn wildcard(category: Category) -> String {
|
|||
pub async fn set<V: for<'a> Deserialize<'a> + Serialize>(
|
||||
key: &str,
|
||||
value: &V,
|
||||
expire_seconds: u64,
|
||||
ttl: Duration,
|
||||
) -> Result<(), Error> {
|
||||
redis_conn()
|
||||
Ok(redis_conn()
|
||||
.await?
|
||||
.set_ex(
|
||||
prefix_key(key),
|
||||
rmp_serde::encode::to_vec(&value)?,
|
||||
expire_seconds,
|
||||
ttl.num_seconds().try_into().map_err(|_| Error::TTL)?,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
.await?)
|
||||
}
|
||||
|
||||
/// Gets a Redis cache.
|
||||
|
@ -103,13 +108,14 @@ pub async fn set<V: for<'a> Deserialize<'a> + Serialize>(
|
|||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use backend_rs::database::cache;
|
||||
/// # use backend_rs::cache;
|
||||
/// use chrono::Duration;
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let key = "banana";
|
||||
/// let data = "I want to cache this string".to_owned();
|
||||
///
|
||||
/// // set cache
|
||||
/// cache::set(key, &data, 10).await?;
|
||||
/// cache::set(key, &data, Duration::seconds(10)).await?;
|
||||
///
|
||||
/// // get cache
|
||||
/// let cached_data = cache::get::<String>(key).await?;
|
||||
|
@ -142,13 +148,14 @@ pub async fn get<V: for<'a> Deserialize<'a> + Serialize>(key: &str) -> Result<Op
|
|||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use backend_rs::database::cache;
|
||||
/// # use backend_rs::cache;
|
||||
/// use chrono::Duration;
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let key = "chocolate";
|
||||
/// let value = "I want to cache this string".to_owned();
|
||||
///
|
||||
/// // set cache
|
||||
/// cache::set(key, &value, 10).await?;
|
||||
/// cache::set(key, &value, Duration::seconds(10)).await?;
|
||||
///
|
||||
/// // delete the cache
|
||||
/// cache::delete("foo").await?;
|
||||
|
@ -174,14 +181,14 @@ pub async fn delete(key: &str) -> Result<(), Error> {
|
|||
/// * `category` : one of [Category]
|
||||
/// * `key` : key (prefixed automatically)
|
||||
/// * `value` : (de)serializable value
|
||||
/// * `expire_seconds` : TTL
|
||||
/// * `ttl` : cache lifetime
|
||||
pub async fn set_one<V: for<'a> Deserialize<'a> + Serialize>(
|
||||
category: Category,
|
||||
key: &str,
|
||||
value: &V,
|
||||
expire_seconds: u64,
|
||||
ttl: Duration,
|
||||
) -> Result<(), Error> {
|
||||
set(&categorize(category, key), value, expire_seconds).await
|
||||
set(&categorize(category, key), value, ttl).await
|
||||
}
|
||||
|
||||
/// Gets a Redis cache under a `category`.
|
||||
|
@ -227,12 +234,11 @@ pub async fn delete_all(category: Category) -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: get_all()
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::{delete_all, get, get_one, set, set_one, Category::Test};
|
||||
use crate::database::cache::delete_one;
|
||||
use crate::cache::delete_one;
|
||||
use chrono::Duration;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[tokio::test]
|
||||
|
@ -256,9 +262,9 @@ mod unit_test {
|
|||
kind: "prime number".to_owned(),
|
||||
};
|
||||
|
||||
set(key_1, &value_1, 1).await.unwrap();
|
||||
set(key_2, &value_2, 1).await.unwrap();
|
||||
set(key_3, &value_3, 1).await.unwrap();
|
||||
set(key_1, &value_1, Duration::seconds(1)).await.unwrap();
|
||||
set(key_2, &value_2, Duration::seconds(1)).await.unwrap();
|
||||
set(key_3, &value_3, Duration::seconds(1)).await.unwrap();
|
||||
|
||||
let cached_value_1: Vec<i32> = get(key_1).await.unwrap().unwrap();
|
||||
let cached_value_2: String = get(key_2).await.unwrap().unwrap();
|
||||
|
@ -291,9 +297,15 @@ mod unit_test {
|
|||
let value_2 = 998244353u32;
|
||||
let value_3 = 'あ';
|
||||
|
||||
set_one(Test, key_1, &value_1, 5 * 60).await.unwrap();
|
||||
set_one(Test, key_2, &value_2, 5 * 60).await.unwrap();
|
||||
set_one(Test, key_3, &value_3, 5 * 60).await.unwrap();
|
||||
set_one(Test, key_1, &value_1, Duration::minutes(5))
|
||||
.await
|
||||
.unwrap();
|
||||
set_one(Test, key_2, &value_2, Duration::minutes(5))
|
||||
.await
|
||||
.unwrap();
|
||||
set_one(Test, key_3, &value_3, Duration::minutes(5))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
get_one::<String>(Test, key_1).await.unwrap().unwrap(),
|
|
@ -1,31 +1,28 @@
|
|||
//! Server information
|
||||
|
||||
use crate::{database::db_conn, model::entity::meta};
|
||||
use crate::{cache::Cache, database::db_conn, model::entity::meta};
|
||||
use chrono::Duration;
|
||||
use sea_orm::{prelude::*, ActiveValue};
|
||||
use std::sync::Mutex;
|
||||
|
||||
type Meta = meta::Model;
|
||||
|
||||
static CACHE: Mutex<Option<Meta>> = Mutex::new(None);
|
||||
fn set_cache(meta: &Meta) {
|
||||
let _ = CACHE.lock().map(|mut cache| *cache = Some(meta.clone()));
|
||||
}
|
||||
static INSTANCE_META_CACHE: Cache<Meta> = Cache::new_with_ttl(Duration::minutes(5));
|
||||
|
||||
#[macros::export(js_name = "fetchMeta")]
|
||||
pub async fn local_server_info() -> Result<Meta, DbErr> {
|
||||
local_server_info_impl(true).await
|
||||
local_server_info_impl(false).await
|
||||
}
|
||||
|
||||
#[macros::export(js_name = "updateMetaCache")]
|
||||
pub async fn update() -> Result<(), DbErr> {
|
||||
local_server_info_impl(false).await?;
|
||||
local_server_info_impl(true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn local_server_info_impl(use_cache: bool) -> Result<Meta, DbErr> {
|
||||
async fn local_server_info_impl(force_update_cache: bool) -> Result<Meta, DbErr> {
|
||||
// try using cache
|
||||
if use_cache {
|
||||
if let Some(cache) = CACHE.lock().ok().and_then(|cache| cache.clone()) {
|
||||
if !force_update_cache {
|
||||
if let Some(cache) = INSTANCE_META_CACHE.get() {
|
||||
return Ok(cache);
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +31,7 @@ async fn local_server_info_impl(use_cache: bool) -> Result<Meta, DbErr> {
|
|||
let db = db_conn().await?;
|
||||
let meta = meta::Entity::find().one(db).await?;
|
||||
if let Some(meta) = meta {
|
||||
set_cache(&meta);
|
||||
INSTANCE_META_CACHE.set(meta.clone());
|
||||
return Ok(meta);
|
||||
}
|
||||
|
||||
|
@ -45,7 +42,7 @@ async fn local_server_info_impl(use_cache: bool) -> Result<Meta, DbErr> {
|
|||
})
|
||||
.exec_with_returning(db)
|
||||
.await?;
|
||||
set_cache(&meta);
|
||||
INSTANCE_META_CACHE.set(meta.clone());
|
||||
Ok(meta)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,5 @@ pub use redis::get_conn as redis_conn;
|
|||
pub use redis::key as redis_key;
|
||||
pub use redis::RedisConnError;
|
||||
|
||||
pub mod cache;
|
||||
pub mod postgresql;
|
||||
pub mod redis;
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
use super::*;
|
||||
use crate::{config::CONFIG, misc::user};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[macros::export(object)]
|
||||
pub struct ApAccept {
|
||||
pub id: String,
|
||||
pub r#type: ApObject,
|
||||
pub actor: String,
|
||||
pub object: follow::ApFollow,
|
||||
}
|
||||
|
||||
impl ActivityPubObject for ApAccept {}
|
||||
|
||||
impl ApAccept {
|
||||
#[allow(dead_code)] // TODO: remove this line
|
||||
fn new(user_id: String, follow_object: follow::ApFollow) -> Self {
|
||||
Self {
|
||||
id: format!("{}/{}", CONFIG.url, Uuid::new_v4()),
|
||||
r#type: ApObject::Accept,
|
||||
actor: user::local_uri(user_id),
|
||||
object: follow_object,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macros::ts_export]
|
||||
pub fn render_accept(user_id: String, follow_object: follow::ApFollow) -> ApAccept {
|
||||
ApAccept::new(user_id, follow_object)
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
use super::*;
|
||||
use crate::{misc, model::entity::emoji};
|
||||
use chrono::Utc;
|
||||
|
||||
#[macros::export(object)]
|
||||
pub struct ApEmoji {
|
||||
pub id: String,
|
||||
pub r#type: ApObject,
|
||||
pub name: String,
|
||||
pub updated: String,
|
||||
pub icon: Icon,
|
||||
}
|
||||
|
||||
#[macros::export(object)]
|
||||
pub struct Icon {
|
||||
pub r#type: ApObject,
|
||||
pub media_type: String,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
impl ActivityPubObject for ApEmoji {}
|
||||
|
||||
impl ApEmoji {
|
||||
#[allow(dead_code)] // TODO: remove this line
|
||||
fn new(emoji: emoji::Model) -> Self {
|
||||
Self {
|
||||
id: misc::emoji::local_uri(&emoji.name),
|
||||
r#type: ApObject::Emoji,
|
||||
name: format!(":{}:", emoji.name),
|
||||
updated: emoji
|
||||
.updated_at
|
||||
.unwrap_or_else(|| Utc::now().into())
|
||||
.to_rfc3339(),
|
||||
icon: Icon {
|
||||
r#type: ApObject::Image,
|
||||
media_type: emoji.r#type.unwrap_or_else(|| "image/png".to_owned()),
|
||||
url: emoji.public_url,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macros::for_ts] // https://github.com/napi-rs/napi-rs/issues/2060
|
||||
type Emoji = emoji::Model;
|
||||
|
||||
#[macros::ts_export]
|
||||
pub fn render_emoji(emoji: Emoji) -> ApEmoji {
|
||||
ApEmoji::new(emoji)
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
use super::*;
|
||||
use crate::{federation::internal_actor, misc::user};
|
||||
|
||||
#[macros::export(object)]
|
||||
pub struct ApFlag {
|
||||
pub r#type: ApObject,
|
||||
pub actor: String,
|
||||
pub content: String,
|
||||
// TODO: object can be an array of uri's
|
||||
pub object: String,
|
||||
}
|
||||
|
||||
impl ActivityPubObject for ApFlag {}
|
||||
|
||||
impl ApFlag {
|
||||
#[allow(dead_code)] // TODO: remove this line
|
||||
async fn new(
|
||||
target_user_uri: String,
|
||||
comment: String,
|
||||
) -> Result<Self, internal_actor::instance::Error> {
|
||||
Ok(Self {
|
||||
r#type: ApObject::Flag,
|
||||
actor: user::local_uri(&internal_actor::instance::get().await?.id),
|
||||
content: comment,
|
||||
object: target_user_uri,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[macros::ts_export]
|
||||
pub async fn render_flag(
|
||||
target_user_uri: String,
|
||||
comment: String,
|
||||
) -> Result<ApFlag, internal_actor::instance::Error> {
|
||||
ApFlag::new(target_user_uri, comment).await
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
use super::*;
|
||||
use crate::{config::CONFIG, federation::internal_actor, misc::user};
|
||||
|
||||
#[macros::export(object)]
|
||||
pub struct ApFollow {
|
||||
pub id: String,
|
||||
pub r#type: ApObject,
|
||||
pub actor: String,
|
||||
pub object: String,
|
||||
}
|
||||
|
||||
impl ActivityPubObject for ApFollow {}
|
||||
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[error("follower uri is missing")]
|
||||
MissingFollowerUri,
|
||||
#[error("followee uri is missing")]
|
||||
MissingFolloweeUri,
|
||||
}
|
||||
|
||||
impl ApFollow {
|
||||
#[allow(dead_code)] // TODO: remove this line
|
||||
fn new(
|
||||
follower: UserLike,
|
||||
followee: UserLike,
|
||||
request_id: Option<String>,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
id: request_id.unwrap_or_else(|| {
|
||||
format!("{}/follows/{}/{}", CONFIG.url, follower.id, followee.id)
|
||||
}),
|
||||
r#type: ApObject::Follow,
|
||||
actor: match user::is_local!(follower) {
|
||||
true => user::local_uri(follower.id),
|
||||
false => follower.uri.ok_or(Error::MissingFollowerUri)?,
|
||||
},
|
||||
object: match user::is_local!(followee) {
|
||||
true => user::local_uri(followee.id),
|
||||
false => followee.uri.ok_or(Error::MissingFolloweeUri)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // TODO: remove this line
|
||||
async fn new_relay(relay_id: String) -> Result<Self, internal_actor::relay::Error> {
|
||||
Ok(Self {
|
||||
id: format!("{}/activities/follow-relay/{}", CONFIG.url, relay_id),
|
||||
r#type: ApObject::Follow,
|
||||
actor: user::local_uri(internal_actor::relay::get_id().await?),
|
||||
object: AS_PUBLIC_URL.to_owned(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[macros::ts_export]
|
||||
pub fn render_follow(
|
||||
follower: UserLike,
|
||||
followee: UserLike,
|
||||
request_id: Option<String>,
|
||||
) -> Result<ApFollow, Error> {
|
||||
ApFollow::new(follower, followee, request_id)
|
||||
}
|
||||
|
||||
#[macros::ts_export]
|
||||
pub async fn render_follow_relay(
|
||||
relay_id: String,
|
||||
) -> Result<ApFollow, internal_actor::relay::Error> {
|
||||
ApFollow::new_relay(relay_id).await
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
use super::*;
|
||||
use crate::config::CONFIG;
|
||||
|
||||
#[macros::export(object)]
|
||||
pub struct ApHashtag {
|
||||
pub id: String,
|
||||
pub r#type: ApObject,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl ActivityPubObject for ApHashtag {}
|
||||
|
||||
impl ApHashtag {
|
||||
#[allow(dead_code)] // TODO: remove this line
|
||||
fn new(tag_name: &str) -> Self {
|
||||
Self {
|
||||
id: format!("{}/tags/{}", CONFIG.url, urlencoding::encode(tag_name)),
|
||||
r#type: ApObject::Hashtag,
|
||||
name: format!("#{}", tag_name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macros::ts_export]
|
||||
pub fn render_hashtag(tag_name: &str) -> ApHashtag {
|
||||
ApHashtag::new(tag_name)
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
use super::*;
|
||||
use crate::{federation::acct::Acct, misc::user};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[error("remote user's uri is missing")]
|
||||
pub struct MissingRemoteUserUri;
|
||||
|
||||
#[macros::export(object)]
|
||||
pub struct ApMention {
|
||||
pub r#type: ApObject,
|
||||
pub href: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl ActivityPubObject for ApMention {}
|
||||
|
||||
impl ApMention {
|
||||
#[allow(dead_code)] // TODO: remove this line
|
||||
fn new(user: UserLike) -> Result<Self, MissingRemoteUserUri> {
|
||||
Ok(Self {
|
||||
r#type: ApObject::Mention,
|
||||
href: match user::is_local!(user) {
|
||||
true => user::local_uri(user.id),
|
||||
false => user.uri.ok_or(MissingRemoteUserUri)?,
|
||||
},
|
||||
name: format!(
|
||||
"@{}",
|
||||
Acct {
|
||||
username: user.username,
|
||||
host: user.host
|
||||
}
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[macros::ts_export]
|
||||
pub fn render_mention(user: UserLike) -> Result<ApMention, MissingRemoteUserUri> {
|
||||
ApMention::new(user)
|
||||
}
|
|
@ -1,9 +1,33 @@
|
|||
pub mod relay;
|
||||
pub mod accept;
|
||||
pub mod emoji;
|
||||
pub mod flag;
|
||||
pub mod follow;
|
||||
pub mod hashtag;
|
||||
pub mod mention;
|
||||
pub mod read;
|
||||
pub mod tombstone;
|
||||
|
||||
pub trait ActivityPubObject {}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[macros::export(string_enum)]
|
||||
pub enum Activity {
|
||||
pub enum ApObject {
|
||||
Accept,
|
||||
Emoji,
|
||||
Flag,
|
||||
Follow,
|
||||
Hashtag,
|
||||
Mention,
|
||||
Image,
|
||||
Read,
|
||||
Tombstone,
|
||||
}
|
||||
|
||||
const AS_PUBLIC_URL: &str = "https://www.w3.org/ns/activitystreams#Public";
|
||||
|
||||
#[macros::export(object)]
|
||||
pub struct UserLike {
|
||||
pub id: String,
|
||||
pub username: String,
|
||||
pub host: Option<String>,
|
||||
pub uri: Option<String>,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
use super::*;
|
||||
use crate::misc::user;
|
||||
|
||||
#[macros::export(object)]
|
||||
pub struct ApRead {
|
||||
pub r#type: ApObject,
|
||||
pub actor: String,
|
||||
pub object: String,
|
||||
}
|
||||
|
||||
impl ActivityPubObject for ApRead {}
|
||||
|
||||
impl ApRead {
|
||||
#[allow(dead_code)] // TODO: remove this line
|
||||
fn new(user_id: String, message_uri: String) -> Self {
|
||||
Self {
|
||||
r#type: ApObject::Read,
|
||||
actor: user::local_uri(user_id),
|
||||
object: message_uri,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macros::ts_export]
|
||||
pub fn render_read(user_id: String, message_uri: String) -> ApRead {
|
||||
ApRead::new(user_id, message_uri)
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
use super::*;
|
||||
use crate::{config::CONFIG, federation::internal_actor};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[macros::export(object)]
|
||||
pub struct FollowRelay {
|
||||
pub id: String,
|
||||
pub r#type: Activity,
|
||||
pub actor: String,
|
||||
pub object: String,
|
||||
}
|
||||
|
||||
impl ActivityPubObject for FollowRelay {}
|
||||
|
||||
#[macros::export(js_name = "renderFollowRelay")]
|
||||
pub async fn follow(relay_id: &str) -> Result<FollowRelay, internal_actor::relay::Error> {
|
||||
Ok(FollowRelay {
|
||||
id: format!("{}/activities/follow-relay/{}", CONFIG.url, relay_id),
|
||||
r#type: Activity::Follow,
|
||||
actor: format!(
|
||||
"{}/users/{}",
|
||||
CONFIG.url,
|
||||
internal_actor::relay::get_id().await?
|
||||
),
|
||||
object: "https://www.w3.org/ns/activitystreams#Public".to_owned(),
|
||||
})
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
use super::*;
|
||||
use crate::misc::note;
|
||||
|
||||
#[macros::export(object)]
|
||||
pub struct ApTombstone {
|
||||
pub id: String,
|
||||
pub r#type: ApObject,
|
||||
}
|
||||
|
||||
impl ActivityPubObject for ApTombstone {}
|
||||
|
||||
impl ApTombstone {
|
||||
#[allow(dead_code)] // TODO: remove this line
|
||||
fn new(note_id: String) -> Self {
|
||||
Self {
|
||||
id: note::local_uri(note_id),
|
||||
r#type: ApObject::Tombstone,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macros::ts_export]
|
||||
pub fn render_tombstone(note_id: String) -> ApTombstone {
|
||||
ApTombstone::new(note_id)
|
||||
}
|
|
@ -4,12 +4,8 @@ use crate::{database::db_conn, model::entity::user};
|
|||
use sea_orm::prelude::*;
|
||||
use tokio::sync::OnceCell;
|
||||
|
||||
// for napi export
|
||||
// https://github.com/napi-rs/napi-rs/issues/2060
|
||||
type User = user::Model;
|
||||
|
||||
pub const USERNAME: &str = "instance.actor";
|
||||
static INSTANCE_ACTOR: OnceCell<User> = OnceCell::const_new();
|
||||
static INSTANCE_ACTOR: OnceCell<user::Model> = OnceCell::const_new();
|
||||
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
|
@ -20,7 +16,7 @@ pub enum Error {
|
|||
Db(#[from] DbErr),
|
||||
}
|
||||
|
||||
async fn set_cache() -> Result<&'static User, Error> {
|
||||
async fn set_cache() -> Result<&'static user::Model, Error> {
|
||||
let instance_actor = INSTANCE_ACTOR
|
||||
.get_or_try_init(|| async {
|
||||
tracing::debug!("caching @instance.actor");
|
||||
|
@ -37,13 +33,16 @@ async fn set_cache() -> Result<&'static User, Error> {
|
|||
Ok(instance_actor)
|
||||
}
|
||||
|
||||
pub async fn get() -> Result<&'static User, Error> {
|
||||
pub async fn get() -> Result<&'static user::Model, Error> {
|
||||
match INSTANCE_ACTOR.get() {
|
||||
Some(model) => Ok(model),
|
||||
None => set_cache().await,
|
||||
}
|
||||
}
|
||||
|
||||
#[macros::for_ts] // https://github.com/napi-rs/napi-rs/issues/2060
|
||||
type User = user::Model;
|
||||
|
||||
#[macros::ts_export(js_name = "getInstanceActor")]
|
||||
pub async fn get_js() -> Result<User, Error> {
|
||||
Ok(get().await?.to_owned())
|
||||
|
|
|
@ -1,23 +1,19 @@
|
|||
//! NodeInfo generator
|
||||
|
||||
use crate::{
|
||||
cache::Cache,
|
||||
config::{local_server_info, CONFIG},
|
||||
database::db_conn,
|
||||
federation::nodeinfo::schema::*,
|
||||
misc,
|
||||
model::entity::{note, user},
|
||||
};
|
||||
use chrono::Duration;
|
||||
use sea_orm::prelude::*;
|
||||
use serde_json::json;
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
use std::collections::HashMap;
|
||||
|
||||
static CACHE: Mutex<Option<Nodeinfo21>> = Mutex::new(None);
|
||||
|
||||
fn set_cache(nodeinfo: &Nodeinfo21) {
|
||||
let _ = CACHE
|
||||
.lock()
|
||||
.map(|mut cache| *cache = Some(nodeinfo.to_owned()));
|
||||
}
|
||||
static NODEINFO_CACHE: Cache<Nodeinfo21> = Cache::new_with_ttl(Duration::hours(1));
|
||||
|
||||
/// Fetches the number of total/active local users and local posts.
|
||||
///
|
||||
|
@ -127,32 +123,26 @@ async fn generate_nodeinfo_2_1() -> Result<Nodeinfo21, DbErr> {
|
|||
})
|
||||
}
|
||||
|
||||
async fn nodeinfo_2_1_impl(use_cache: bool) -> Result<Nodeinfo21, DbErr> {
|
||||
if use_cache {
|
||||
if let Some(nodeinfo) = CACHE.lock().ok().and_then(|cache| cache.to_owned()) {
|
||||
return Ok(nodeinfo);
|
||||
}
|
||||
/// Returns NodeInfo (version 2.1) of the local server.
|
||||
pub async fn nodeinfo_2_1() -> Result<Nodeinfo21, DbErr> {
|
||||
if let Some(nodeinfo) = NODEINFO_CACHE.get() {
|
||||
return Ok(nodeinfo);
|
||||
}
|
||||
|
||||
let nodeinfo = generate_nodeinfo_2_1().await?;
|
||||
|
||||
tracing::info!("updating cache");
|
||||
set_cache(&nodeinfo);
|
||||
NODEINFO_CACHE.set(nodeinfo.clone());
|
||||
|
||||
Ok(nodeinfo)
|
||||
}
|
||||
|
||||
/// Returns NodeInfo (version 2.1) of the local server.
|
||||
pub async fn nodeinfo_2_1() -> Result<Nodeinfo21, DbErr> {
|
||||
nodeinfo_2_1_impl(true).await
|
||||
}
|
||||
|
||||
/// Returns NodeInfo (version 2.0) of the local server.
|
||||
pub async fn nodeinfo_2_0() -> Result<Nodeinfo20, DbErr> {
|
||||
Ok(nodeinfo_2_1().await?.into())
|
||||
}
|
||||
|
||||
#[cfg(any(test, doctest, feature = "napi"))]
|
||||
#[macros::for_ts]
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[doc = "database error"]
|
||||
|
@ -171,9 +161,3 @@ pub async fn nodeinfo_2_1_as_json() -> Result<serde_json::Value, Error> {
|
|||
pub async fn nodeinfo_2_0_as_json() -> Result<serde_json::Value, Error> {
|
||||
Ok(serde_json::to_value(nodeinfo_2_0().await?)?)
|
||||
}
|
||||
|
||||
#[macros::ts_export(js_name = "updateNodeinfoCache")]
|
||||
pub async fn update_cache() -> Result<(), DbErr> {
|
||||
nodeinfo_2_1_impl(false).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,28 +1,10 @@
|
|||
use std::sync::{Mutex, MutexGuard, OnceLock, PoisonError};
|
||||
use crate::misc::system_info;
|
||||
use sysinfo::System;
|
||||
|
||||
pub type SysinfoPoisonError = PoisonError<MutexGuard<'static, System>>;
|
||||
|
||||
static SYSTEM_INFO: OnceLock<Mutex<System>> = OnceLock::new();
|
||||
|
||||
/// Gives an access to the shared static [System] object.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use backend_rs::init::system_info::{system_info, SysinfoPoisonError};
|
||||
/// let system_info = system_info().lock()?;
|
||||
/// println!("The number of CPU threads is {}.", system_info.cpus().len());
|
||||
/// # Ok::<(), SysinfoPoisonError>(())
|
||||
/// ```
|
||||
pub fn system_info() -> &'static std::sync::Mutex<System> {
|
||||
SYSTEM_INFO.get_or_init(|| Mutex::new(System::new_all()))
|
||||
}
|
||||
|
||||
/// Prints the server hardware information as the server info log.
|
||||
#[macros::export]
|
||||
pub fn show_server_info() -> Result<(), SysinfoPoisonError> {
|
||||
let system_info = system_info().lock()?;
|
||||
pub fn show_server_info() {
|
||||
let system_info = system_info::get();
|
||||
|
||||
tracing::info!(
|
||||
"Hostname: {}",
|
||||
|
@ -45,6 +27,4 @@ pub fn show_server_info() -> Result<(), SysinfoPoisonError> {
|
|||
tracing::info!("Free memory: {} MiB", system_info.free_memory() / 1048576);
|
||||
tracing::info!("Total swap: {} MiB", system_info.total_swap() / 1048576);
|
||||
tracing::info!("Free swap: {} MiB", system_info.free_swap() / 1048576);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
|
||||
|
||||
pub mod cache;
|
||||
pub mod config;
|
||||
pub mod database;
|
||||
pub mod federation;
|
||||
|
|
9
packages/backend-rs/src/misc/emoji/mod.rs
Normal file
9
packages/backend-rs/src/misc/emoji/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
pub mod reaction;
|
||||
pub mod unicode;
|
||||
|
||||
use crate::config::CONFIG;
|
||||
|
||||
/// Returns URI of a local custom emoji.
|
||||
pub fn local_uri(emoji_code: impl std::fmt::Display) -> String {
|
||||
format!("{}/emojis/{}", CONFIG.url, emoji_code)
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{database::cache, util::http_client};
|
||||
use crate::{cache, util::http_client};
|
||||
use chrono::Duration;
|
||||
use futures_util::AsyncReadExt;
|
||||
use image::{ImageError, ImageFormat, ImageReader};
|
||||
use isahc::prelude::*;
|
||||
use isahc::AsyncReadResponseExt;
|
||||
use nom_exif::{parse_jpeg_exif, EntryValue, ExifTag};
|
||||
use std::io::Cursor;
|
||||
use tokio::sync::Mutex;
|
||||
|
@ -9,7 +10,7 @@ use tokio::sync::Mutex;
|
|||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[error("Redis cache operation has failed")]
|
||||
Cache(#[from] cache::Error),
|
||||
Cache(#[from] cache::redis::Error),
|
||||
#[error("failed to acquire an HTTP client")]
|
||||
HttpClient(#[from] http_client::Error),
|
||||
#[error("HTTP request failed")]
|
||||
|
@ -63,7 +64,7 @@ pub async fn get_image_size_from_url(url: &str) -> Result<ImageSize, Error> {
|
|||
.is_some();
|
||||
|
||||
if !attempted {
|
||||
cache::set_one(cache::Category::FetchUrl, url, &true, 10 * 60).await?;
|
||||
cache::set_one(cache::Category::FetchUrl, url, &true, Duration::minutes(10)).await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,7 +135,7 @@ pub async fn get_image_size_from_url(url: &str) -> Result<ImageSize, Error> {
|
|||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::ImageSize;
|
||||
use crate::database::cache;
|
||||
use crate::cache;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
//! Fetch latest Firefish version from the Firefish repository
|
||||
|
||||
use crate::{database::cache, util::http_client};
|
||||
use crate::{cache::Cache, util::http_client};
|
||||
use chrono::Duration;
|
||||
use futures_util::AsyncReadExt;
|
||||
use isahc::AsyncReadResponseExt;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[error("Redis cache operation has failed")]
|
||||
Cache(#[from] cache::Error),
|
||||
#[error("HTTP request failed")]
|
||||
Isahc(#[from] isahc::Error),
|
||||
#[error("failed to acquire an HTTP client")]
|
||||
|
@ -22,15 +21,17 @@ pub enum Error {
|
|||
Json(#[from] serde_json::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
struct PackageJson {
|
||||
version: String,
|
||||
}
|
||||
|
||||
const UPSTREAM_PACKAGE_JSON_URL: &str =
|
||||
"https://firefish.dev/firefish/firefish/-/raw/main/package.json";
|
||||
|
||||
async fn get_latest_version() -> Result<String, Error> {
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Response {
|
||||
version: String,
|
||||
}
|
||||
static PACKAGE_JSON_CACHE: Cache<PackageJson> = Cache::new_with_ttl(Duration::hours(3));
|
||||
|
||||
async fn get_package_json() -> Result<PackageJson, Error> {
|
||||
// Read up to 1 MiB of the response body
|
||||
let mut response = http_client::client()?
|
||||
.get_async(UPSTREAM_PACKAGE_JSON_URL)
|
||||
|
@ -42,42 +43,33 @@ async fn get_latest_version() -> Result<String, Error> {
|
|||
return Err(Error::BadStatus(response.status().to_string()));
|
||||
}
|
||||
|
||||
let res_parsed: Response = serde_json::from_str(&response.text().await?)?;
|
||||
let package_json: PackageJson = serde_json::from_str(&response.text().await?)?;
|
||||
|
||||
Ok(res_parsed.version)
|
||||
Ok(package_json)
|
||||
}
|
||||
|
||||
/// Returns the latest Firefish version.
|
||||
#[macros::export]
|
||||
pub async fn latest_version() -> Result<String, Error> {
|
||||
let version: Option<String> =
|
||||
cache::get_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL).await?;
|
||||
|
||||
if let Some(v) = version {
|
||||
tracing::trace!("use cached value: {}", v);
|
||||
Ok(v)
|
||||
if let Some(package_json) = PACKAGE_JSON_CACHE.get() {
|
||||
tracing::trace!("use cached value: {}", package_json.version);
|
||||
Ok(package_json.version)
|
||||
} else {
|
||||
tracing::trace!("cache is expired, fetching the latest version");
|
||||
let fetched_version = get_latest_version().await?;
|
||||
tracing::trace!("fetched value: {}", fetched_version);
|
||||
let package_json = get_package_json().await?;
|
||||
tracing::trace!("fetched value: {}", package_json.version);
|
||||
|
||||
cache::set_one(
|
||||
cache::Category::FetchUrl,
|
||||
UPSTREAM_PACKAGE_JSON_URL,
|
||||
&fetched_version,
|
||||
3 * 60 * 60,
|
||||
)
|
||||
.await?;
|
||||
Ok(fetched_version)
|
||||
PACKAGE_JSON_CACHE.set(package_json.clone());
|
||||
Ok(package_json.version)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::{latest_version, UPSTREAM_PACKAGE_JSON_URL};
|
||||
use crate::database::cache;
|
||||
use super::latest_version;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
fn validate_version(version: String) {
|
||||
fn validate_version(version: &str) {
|
||||
// version: YYYYMMDD or YYYYMMDD-X
|
||||
assert!(version.len() >= 8);
|
||||
assert!(version[..8].chars().all(|c| c.is_ascii_digit()));
|
||||
|
@ -103,15 +95,14 @@ mod unit_test {
|
|||
#[tokio::test]
|
||||
#[cfg_attr(miri, ignore)] // can't call foreign function `getaddrinfo` on OS `linux`
|
||||
async fn get_latest_version() {
|
||||
// delete caches in case you run this test multiple times
|
||||
cache::delete_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// fetch from firefish.dev
|
||||
validate_version(latest_version().await.unwrap());
|
||||
let version_1 = latest_version().await.unwrap();
|
||||
validate_version(&version_1);
|
||||
|
||||
// use cache
|
||||
validate_version(latest_version().await.unwrap());
|
||||
let version_2 = latest_version().await.unwrap();
|
||||
validate_version(&version_2);
|
||||
|
||||
assert_eq!(version_1, version_2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ pub mod latest_version;
|
|||
pub mod note;
|
||||
pub mod nyaify;
|
||||
pub mod password;
|
||||
pub mod reaction;
|
||||
pub mod random_icon;
|
||||
pub mod remove_old_attestation_challenges;
|
||||
pub mod should_nyaify;
|
||||
pub mod system_info;
|
||||
|
|
|
@ -3,3 +3,10 @@ pub use summarize::summarize;
|
|||
|
||||
pub mod elaborate;
|
||||
pub mod summarize;
|
||||
|
||||
use crate::config::CONFIG;
|
||||
|
||||
/// Returns URI of a local post.
|
||||
pub fn local_uri(note_id: impl std::fmt::Display) -> String {
|
||||
format!("{}/notes/{}", CONFIG.url, note_id)
|
||||
}
|
||||
|
|
43
packages/backend-rs/src/misc/random_icon.rs
Normal file
43
packages/backend-rs/src/misc/random_icon.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use crate::cache;
|
||||
use chrono::Duration;
|
||||
use identicon_rs::{error::IdenticonError, Identicon};
|
||||
|
||||
#[macros::errors]
|
||||
pub enum Error {
|
||||
#[doc = "failed to generate identicon"]
|
||||
#[error(transparent)]
|
||||
Identicon(#[from] IdenticonError),
|
||||
#[error("Redis cache operation has failed")]
|
||||
Cache(#[from] cache::redis::Error),
|
||||
}
|
||||
|
||||
pub async fn generate(id: &str) -> Result<Vec<u8>, Error> {
|
||||
if let Some(icon) = cache::get_one::<Vec<u8>>(cache::Category::RandomIcon, id).await? {
|
||||
Ok(icon)
|
||||
} else {
|
||||
let icon = Identicon::new(id)
|
||||
.set_border(16)
|
||||
.set_scale(96)?
|
||||
.export_png_data()?;
|
||||
cache::set_one(
|
||||
cache::Category::RandomIcon,
|
||||
id,
|
||||
&icon,
|
||||
Duration::minutes(10),
|
||||
)
|
||||
.await?;
|
||||
Ok(icon)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi")]
|
||||
#[napi_derive::napi(js_name = "genIdenticon")]
|
||||
pub async fn generate_js(id: String) -> napi::Result<napi::bindgen_prelude::Buffer> {
|
||||
match generate(&id).await {
|
||||
Ok(icon) => Ok(icon.into()),
|
||||
Err(err) => Err(napi::Error::from_reason(format!(
|
||||
"\n{}\n",
|
||||
crate::util::error_chain::format_error(&err)
|
||||
))),
|
||||
}
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
//! Determine whether to enable the cat language conversion
|
||||
|
||||
use crate::{
|
||||
database::{cache, db_conn},
|
||||
model::entity::user,
|
||||
};
|
||||
use crate::{cache, database::db_conn, model::entity::user};
|
||||
use chrono::Duration;
|
||||
use sea_orm::{DbErr, EntityTrait, QuerySelect, SelectColumns};
|
||||
|
||||
#[macros::errors]
|
||||
|
@ -13,7 +11,8 @@ pub enum Error {
|
|||
Db(#[from] DbErr),
|
||||
#[doc = "cache error"]
|
||||
#[error(transparent)]
|
||||
Cache(#[from] cache::Error),
|
||||
Cache(#[from] cache::redis::Error),
|
||||
#[doc = "user not found"]
|
||||
#[error("user {0} not found")]
|
||||
NotFound(String),
|
||||
}
|
||||
|
@ -37,7 +36,7 @@ pub async fn should_nyaify(reader_user_id: &str) -> Result<bool, Error> {
|
|||
cache::Category::CatLang,
|
||||
reader_user_id,
|
||||
&fetched_value,
|
||||
10 * 60,
|
||||
Duration::minutes(10),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -1,10 +1,35 @@
|
|||
//! Utilities to check hardware information such as cpu, memory, storage usage
|
||||
|
||||
use crate::init::system_info::{system_info, SysinfoPoisonError};
|
||||
use sysinfo::{Disks, MemoryRefreshKind};
|
||||
|
||||
// TODO: i64 -> u64 (we can't export u64 to Node.js)
|
||||
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
use sysinfo::{Disks, MemoryRefreshKind, System};
|
||||
|
||||
static SYSTEM_INFO: OnceLock<Mutex<System>> = OnceLock::new();
|
||||
|
||||
/// Gives you access to the shared static [System] object.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use backend_rs::misc::system_info;
|
||||
/// let system_info = system_info::get();
|
||||
/// println!("The number of CPU threads is {}.", system_info.cpus().len());
|
||||
/// println!("The total memory is {} MiB.", system_info.total_memory() / 1048576);
|
||||
/// ```
|
||||
pub fn get() -> std::sync::MutexGuard<'static, System> {
|
||||
let guard = SYSTEM_INFO
|
||||
.get_or_init(|| Mutex::new(System::new_all()))
|
||||
.lock();
|
||||
|
||||
if let Err(err) = guard {
|
||||
let mut inner = err.into_inner();
|
||||
*inner = System::new_all();
|
||||
inner
|
||||
} else {
|
||||
guard.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[macros::export(object)]
|
||||
pub struct Cpu {
|
||||
pub model: String,
|
||||
|
@ -31,10 +56,10 @@ pub struct Storage {
|
|||
}
|
||||
|
||||
#[macros::export]
|
||||
pub fn cpu_info() -> Result<Cpu, SysinfoPoisonError> {
|
||||
let system_info = system_info().lock()?;
|
||||
pub fn cpu_info() -> Cpu {
|
||||
let system_info = get();
|
||||
|
||||
Ok(Cpu {
|
||||
Cpu {
|
||||
model: match system_info.cpus() {
|
||||
[] => {
|
||||
tracing::debug!("failed to get CPU info");
|
||||
|
@ -43,31 +68,31 @@ pub fn cpu_info() -> Result<Cpu, SysinfoPoisonError> {
|
|||
cpus => cpus[0].brand().to_owned(),
|
||||
},
|
||||
cores: system_info.cpus().len() as u16,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[macros::export]
|
||||
pub fn cpu_usage() -> Result<f32, SysinfoPoisonError> {
|
||||
let mut system_info = system_info().lock()?;
|
||||
pub fn cpu_usage() -> f32 {
|
||||
let mut system_info = get();
|
||||
system_info.refresh_cpu_usage();
|
||||
|
||||
let total_cpu_usage: f32 = system_info.cpus().iter().map(|cpu| cpu.cpu_usage()).sum();
|
||||
let cpu_threads = system_info.cpus().len();
|
||||
|
||||
Ok(total_cpu_usage / (cpu_threads as f32))
|
||||
total_cpu_usage / (cpu_threads as f32)
|
||||
}
|
||||
|
||||
#[macros::export]
|
||||
pub fn memory_usage() -> Result<Memory, SysinfoPoisonError> {
|
||||
let mut system_info = system_info().lock()?;
|
||||
pub fn memory_usage() -> Memory {
|
||||
let mut system_info = get();
|
||||
|
||||
system_info.refresh_memory_specifics(MemoryRefreshKind::new().with_ram());
|
||||
|
||||
Ok(Memory {
|
||||
Memory {
|
||||
total: system_info.total_memory() as i64,
|
||||
used: system_info.used_memory() as i64,
|
||||
available: system_info.available_memory() as i64,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[macros::export]
|
||||
|
|
|
@ -1 +1,30 @@
|
|||
pub mod count;
|
||||
|
||||
use crate::config::CONFIG;
|
||||
|
||||
/// Returns URI of a local user.
|
||||
pub fn local_uri(user_id: impl std::fmt::Display) -> String {
|
||||
format!("{}/users/{}", CONFIG.url, user_id)
|
||||
}
|
||||
|
||||
#[doc(hidden)] // hide the macro in the top doc page
|
||||
#[macro_export]
|
||||
macro_rules! is_local {
|
||||
($user_like:expr) => {
|
||||
$user_like.host.is_none()
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(inline)] // show the macro in the module doc page
|
||||
pub use is_local;
|
||||
|
||||
#[doc(hidden)] // hide the macro in the top doc page
|
||||
#[macro_export]
|
||||
macro_rules! is_remote {
|
||||
($user_like:expr) => {
|
||||
$user_like.host.is_some()
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(inline)] // show the macro in the module doc page
|
||||
pub use is_remote;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use super::sea_orm_active_enums::AntennaSrc;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use super::sea_orm_active_enums::DriveFileUsageHint;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
@ -31,11 +31,11 @@ pub struct Model {
|
|||
pub thumbnail_url: Option<String>,
|
||||
#[sea_orm(column_name = "webpublicUrl")]
|
||||
pub webpublic_url: Option<String>,
|
||||
#[sea_orm(column_name = "accessKey")]
|
||||
#[sea_orm(column_name = "accessKey", unique)]
|
||||
pub access_key: Option<String>,
|
||||
#[sea_orm(column_name = "thumbnailAccessKey")]
|
||||
#[sea_orm(column_name = "thumbnailAccessKey", unique)]
|
||||
pub thumbnail_access_key: Option<String>,
|
||||
#[sea_orm(column_name = "webpublicAccessKey")]
|
||||
#[sea_orm(column_name = "webpublicAccessKey", unique)]
|
||||
pub webpublic_access_key: Option<String>,
|
||||
pub uri: Option<String>,
|
||||
pub src: Option<String>,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
|
|||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(unique)]
|
||||
pub name: String,
|
||||
#[sea_orm(column_name = "mentionedUserIds")]
|
||||
pub mentioned_user_ids: Vec<String>,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -12,6 +12,7 @@ pub struct Model {
|
|||
pub id: String,
|
||||
#[sea_orm(column_name = "caughtAt")]
|
||||
pub caught_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(unique)]
|
||||
pub host: String,
|
||||
#[sea_orm(column_name = "usersCount")]
|
||||
pub users_count: i32,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use super::sea_orm_active_enums::MutedNoteReason;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use super::sea_orm_active_enums::NoteVisibility;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
@ -33,6 +33,7 @@ pub struct Model {
|
|||
#[sea_orm(column_type = "JsonBinary")]
|
||||
pub reactions: Json,
|
||||
pub visibility: NoteVisibility,
|
||||
#[sea_orm(unique)]
|
||||
pub uri: Option<String>,
|
||||
pub score: i32,
|
||||
#[sea_orm(column_name = "fileIds")]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use super::sea_orm_active_enums::NotificationType;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use super::sea_orm_active_enums::PageVisibility;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -12,6 +12,7 @@ pub struct Model {
|
|||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(unique)]
|
||||
pub token: String,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use super::sea_orm_active_enums::PollNoteVisibility;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
pub use super::abuse_user_report::Entity as AbuseUserReport;
|
||||
pub use super::access_token::Entity as AccessToken;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue