Merge branch 'develop' into refactor/scheduled-posts
This commit is contained in:
commit
167d89e03e
195 changed files with 2323 additions and 2270 deletions
|
@ -6,5 +6,9 @@ indent_size = 2
|
|||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
|
||||
[*.rs]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -15,6 +15,7 @@ node_modules
|
|||
report.*.json
|
||||
|
||||
# Cargo
|
||||
/.cargo
|
||||
/target
|
||||
|
||||
# Cypress
|
||||
|
|
|
@ -25,10 +25,11 @@ workflow:
|
|||
- when: never
|
||||
|
||||
stages:
|
||||
- dependency
|
||||
- test
|
||||
- doc
|
||||
- build
|
||||
- dependency
|
||||
- clean
|
||||
|
||||
variables:
|
||||
POSTGRES_DB: 'firefish_db'
|
||||
|
@ -199,6 +200,8 @@ build:container:
|
|||
STORAGE_DRIVER: overlay
|
||||
before_script:
|
||||
- apt-get update && apt-get -y upgrade
|
||||
- |-
|
||||
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="${CI_REGISTRY}/${CI_PROJECT_PATH}/develop:not-for-production"
|
||||
|
@ -285,12 +288,12 @@ cargo:doc:
|
|||
- 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
|
||||
- npm install --global netlify-cli
|
||||
script:
|
||||
- cargo doc --no-deps --document-private-items
|
||||
- cargo doc --document-private-items
|
||||
- printf "window.ALL_CRATES = ['backend_rs', 'macro_rs'];" > target/doc/crates.js
|
||||
- printf '<meta http-equiv="refresh" content="0; url=%s">' 'backend_rs' > target/doc/index.html
|
||||
- cd target/doc
|
||||
- netlify deploy --prod --site="${CARGO_DOC_SITE_ID}" --dir=.
|
||||
- npx --yes netlify-cli deploy --prod --site="${CARGO_DOC_SITE_ID}" --dir=.
|
||||
|
||||
renovate:
|
||||
stage: dependency
|
||||
|
@ -303,3 +306,19 @@ renovate:
|
|||
before_script: []
|
||||
script:
|
||||
- renovate --platform gitlab --token "${API_TOKEN}" --endpoint "${CI_SERVER_URL}/api/v4" "${CI_PROJECT_PATH}"
|
||||
|
||||
clean:
|
||||
stage: clean
|
||||
rules:
|
||||
- if: $CLEAN && $CI_PIPELINE_SOURCE == 'schedule'
|
||||
services: []
|
||||
before_script:
|
||||
- apt-get update && apt-get -y upgrade
|
||||
- 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
|
||||
- corepack prepare pnpm@latest --activate
|
||||
- pnpm install --frozen-lockfile
|
||||
script:
|
||||
- pnpm run clean-all
|
||||
|
|
393
Cargo.lock
generated
393
Cargo.lock
generated
|
@ -17,17 +17,6 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
|
@ -309,7 +298,6 @@ dependencies = [
|
|||
"async-trait",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"parking_lot",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
@ -323,18 +311,6 @@ dependencies = [
|
|||
"blowfish",
|
||||
"getrandom",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bigdecimal"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -343,12 +319,6 @@ 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"
|
||||
|
@ -370,18 +340,6 @@ version = "2.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c12d1856e42f0d817a835fe55853957c85c8c8a470114029143d3f12671446e"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
|
@ -410,30 +368,6 @@ dependencies = [
|
|||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbe5b10e214954177fb1dc9fbd20a1a2608fe99e6c832033bdc7cea287a20d77"
|
||||
dependencies = [
|
||||
"borsh-derive",
|
||||
"cfg_aliases",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh-derive"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7a8646f94ab393e43e8b35a2558b1624bed28b97ee09c5d15456e3c9463f46d"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "built"
|
||||
version = "0.7.3"
|
||||
|
@ -446,28 +380,6 @@ version = "3.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
|
||||
dependencies = [
|
||||
"bytecheck_derive",
|
||||
"ptr_meta",
|
||||
"simdutf8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck_derive"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.16.0"
|
||||
|
@ -525,12 +437,6 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.38"
|
||||
|
@ -690,12 +596,6 @@ 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"
|
||||
|
@ -995,22 +895,6 @@ 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"
|
||||
|
@ -1102,12 +986,6 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.30"
|
||||
|
@ -1261,32 +1139,13 @@ 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.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash 0.7.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"ahash",
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
|
@ -1296,7 +1155,7 @@ version = "0.8.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1430,14 +1289,11 @@ dependencies = [
|
|||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"image-webp",
|
||||
"num-traits",
|
||||
"png",
|
||||
"qoi",
|
||||
"ravif",
|
||||
"rayon",
|
||||
"rgb",
|
||||
"tiff",
|
||||
"zune-core",
|
||||
|
@ -1467,7 +1323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1634,12 +1490,6 @@ dependencies = [
|
|||
"spin 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
|
@ -1743,6 +1593,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"convert_case",
|
||||
"napi",
|
||||
"napi-derive",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
|
@ -2420,15 +2271,6 @@ dependencies = [
|
|||
"elliptic-curve",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
|
||||
dependencies = [
|
||||
"toml_edit 0.21.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
|
@ -2455,9 +2297,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.84"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6"
|
||||
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -2481,35 +2323,6 @@ dependencies = [
|
|||
"syn 2.0.66",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ptr_meta"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
|
||||
dependencies = [
|
||||
"ptr_meta_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ptr_meta_derive"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "2.0.1"
|
||||
|
@ -2525,12 +2338,6 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
|
@ -2633,9 +2440,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "0.25.3"
|
||||
version = "0.25.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6472825949c09872e8f2c50bde59fcefc17748b6be5c90fd67cd8b4daca73bfd"
|
||||
checksum = "e0d7a6955c7511f60f3ba9e86c6d02b3c3f144f8c24b288d1f4e18074ab8bbec"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
|
@ -2697,15 +2504,6 @@ version = "0.8.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
||||
|
||||
[[package]]
|
||||
name = "rend"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
|
||||
dependencies = [
|
||||
"bytecheck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfc6979"
|
||||
version = "0.4.0"
|
||||
|
@ -2740,35 +2538,6 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv"
|
||||
version = "0.7.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"bytecheck",
|
||||
"bytes",
|
||||
"hashbrown 0.12.3",
|
||||
"ptr_meta",
|
||||
"rend",
|
||||
"rkyv_derive",
|
||||
"seahash",
|
||||
"tinyvec",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv_derive"
|
||||
version = "0.7.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmp"
|
||||
version = "0.8.14"
|
||||
|
@ -2832,22 +2601,6 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust_decimal"
|
||||
version = "1.35.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"borsh",
|
||||
"bytes",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rkyv",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
|
@ -2955,12 +2708,10 @@ checksum = "c8814e37dc25de54398ee62228323657520b7f29713b8e238649385dbe473ee0"
|
|||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
"bigdecimal",
|
||||
"chrono",
|
||||
"futures",
|
||||
"log",
|
||||
"ouroboros",
|
||||
"rust_decimal",
|
||||
"sea-orm-macros",
|
||||
"sea-query",
|
||||
"sea-query-binder",
|
||||
|
@ -2995,15 +2746,11 @@ version = "0.30.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4166a1e072292d46dc91f31617c2a1cdaf55a8be4b5c9f4bf2ba248e3ac4999b"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"chrono",
|
||||
"derivative",
|
||||
"inherent",
|
||||
"ordered-float",
|
||||
"rust_decimal",
|
||||
"serde_json",
|
||||
"time",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3012,22 +2759,12 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36bbb68df92e820e4d5aeb17b4acd5cc8b5d18b2c36a4dd6f4626aabfa7ab1b9"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"chrono",
|
||||
"rust_decimal",
|
||||
"sea-query",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"time",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "seahash"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.7.3"
|
||||
|
@ -3197,12 +2934,6 @@ dependencies = [
|
|||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simdutf8"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.11"
|
||||
|
@ -3310,9 +3041,8 @@ version = "0.7.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"ahash",
|
||||
"atoi",
|
||||
"bigdecimal",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
|
@ -3333,7 +3063,6 @@ dependencies = [
|
|||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rust_decimal",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
|
@ -3342,12 +3071,10 @@ dependencies = [
|
|||
"smallvec",
|
||||
"sqlformat",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
|
@ -3398,7 +3125,6 @@ checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
|
|||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.21.7",
|
||||
"bigdecimal",
|
||||
"bitflags 2.5.0",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
|
@ -3423,7 +3149,6 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"rand",
|
||||
"rsa 0.9.6",
|
||||
"rust_decimal",
|
||||
"serde",
|
||||
"sha1",
|
||||
"sha2",
|
||||
|
@ -3431,9 +3156,7 @@ dependencies = [
|
|||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tracing",
|
||||
"uuid",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
|
@ -3445,7 +3168,6 @@ checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
|
|||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.21.7",
|
||||
"bigdecimal",
|
||||
"bitflags 2.5.0",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
|
@ -3464,10 +3186,8 @@ dependencies = [
|
|||
"log",
|
||||
"md-5",
|
||||
"memchr",
|
||||
"num-bigint",
|
||||
"once_cell",
|
||||
"rand",
|
||||
"rust_decimal",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
|
@ -3475,9 +3195,7 @@ dependencies = [
|
|||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tracing",
|
||||
"uuid",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
|
@ -3500,11 +3218,9 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"serde",
|
||||
"sqlx-core",
|
||||
"time",
|
||||
"tracing",
|
||||
"url",
|
||||
"urlencoding",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3580,18 +3296,6 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn_derive"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.6"
|
||||
|
@ -3615,7 +3319,6 @@ dependencies = [
|
|||
"libc",
|
||||
"ntapi",
|
||||
"once_cell",
|
||||
"rayon",
|
||||
"windows",
|
||||
]
|
||||
|
||||
|
@ -3632,12 +3335,6 @@ dependencies = [
|
|||
"version-compare",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.14"
|
||||
|
@ -3704,7 +3401,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
|
@ -3745,16 +3441,15 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.37.0"
|
||||
version = "1.38.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
|
||||
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
|
@ -3764,9 +3459,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -3819,7 +3514,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.22.13",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3831,17 +3526,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
"winnow 0.5.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.13"
|
||||
|
@ -3852,7 +3536,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow 0.6.8",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3885,7 +3569,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3898,17 +3581,6 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.18"
|
||||
|
@ -3917,10 +3589,8 @@ checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
|||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4017,12 +3687,6 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
|
@ -4345,15 +4009,6 @@ version = "0.52.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.8"
|
||||
|
@ -4363,15 +4018,6 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
|
@ -4410,15 +4056,6 @@ version = "0.4.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.4.11"
|
||||
|
|
74
Cargo.toml
74
Cargo.toml
|
@ -5,47 +5,47 @@ resolver = "2"
|
|||
[workspace.dependencies]
|
||||
macro-rs = { path = "packages/macro-rs" }
|
||||
|
||||
napi = { git = "https://github.com/napi-rs/napi-rs.git", rev = "ca2cd5c35a0c39ec4a94e93c6c5695b681046df2", default-features = false }
|
||||
napi = { git = "https://github.com/napi-rs/napi-rs.git", rev = "ca2cd5c35a0c39ec4a94e93c6c5695b681046df2" }
|
||||
napi-derive = "2.16.5"
|
||||
napi-build = "2.1.3"
|
||||
|
||||
argon2 = "0.5.3"
|
||||
async-trait = "0.1.80"
|
||||
basen = "0.1.0"
|
||||
bb8 = "0.8.3"
|
||||
bcrypt = "0.15.1"
|
||||
chrono = "0.4.38"
|
||||
convert_case = "0.6.0"
|
||||
cuid2 = "0.1.2"
|
||||
emojis = "0.6.2"
|
||||
idna = "0.5.0"
|
||||
image = "0.25.1"
|
||||
isahc = "1.7.2"
|
||||
nom-exif = "1.2.0"
|
||||
once_cell = "1.19.0"
|
||||
argon2 = { version = "0.5.3", default-features = false }
|
||||
async-trait = { version = "0.1.80", default-features = false }
|
||||
basen = { version = "0.1.0", default-features = false }
|
||||
bb8 = { version = "0.8.3", default-features = false }
|
||||
bcrypt = { version = "0.15.1", default-features = false }
|
||||
chrono = { version = "0.4.38", default-features = false }
|
||||
convert_case = { version = "0.6.0", default-features = false }
|
||||
cuid2 = { version = "0.1.2", default-features = false }
|
||||
emojis = { version = "0.6.2", default-features = false }
|
||||
idna = { version = "0.5.0", default-features = false }
|
||||
image = { version = "0.25.1", default-features = false }
|
||||
isahc = { version = "1.7.2", default-features = false }
|
||||
nom-exif = { version = "1.2.0", default-features = false }
|
||||
once_cell = { version = "1.19.0", default-features = false }
|
||||
openssl = "0.10.64"
|
||||
pretty_assertions = "1.4.0"
|
||||
proc-macro2 = "1.0.84"
|
||||
quote = "1.0.36"
|
||||
rand = "0.8.5"
|
||||
redis = { version = "0.25.3", default-features = false }
|
||||
regex = "1.10.4"
|
||||
rmp-serde = "1.3.0"
|
||||
sea-orm = "0.12.15"
|
||||
serde = "1.0.203"
|
||||
serde_json = "1.0.117"
|
||||
serde_yaml = "0.9.34"
|
||||
strum = "0.26.2"
|
||||
syn = "2.0.66"
|
||||
sysinfo = "0.30.12"
|
||||
thiserror = "1.0.61"
|
||||
tokio = "1.37.0"
|
||||
tokio-test = "0.4.4"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
url = "2.5.0"
|
||||
urlencoding = "2.1.3"
|
||||
web-push = { git = "https://github.com/pimeys/rust-web-push.git", rev = "40febe4085e3cef9cdfd539c315e3e945aba0656" }
|
||||
pretty_assertions = { version = "1.4.0", default-features = false }
|
||||
proc-macro2 = { version = "1.0.85", default-features = false }
|
||||
quote = { version = "1.0.36", default-features = false }
|
||||
rand = { version = "0.8.5", default-features = false }
|
||||
redis = { version = "0.25.4", default-features = false }
|
||||
regex = { version = "1.10.4", 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.203", default-features = false }
|
||||
serde_json = { version = "1.0.117", default-features = false }
|
||||
serde_yaml = { version = "0.9.34", default-features = false }
|
||||
strum = { version = "0.26.2", default-features = false }
|
||||
syn = { version = "2.0.66", default-features = false }
|
||||
sysinfo = { version = "0.30.12", default-features = false }
|
||||
thiserror = { version = "1.0.61", default-features = false }
|
||||
tokio = { version = "1.38.0", default-features = false }
|
||||
tokio-test = { version = "0.4.4", default-features = false }
|
||||
tracing = { version = "0.1.40", default-features = false }
|
||||
tracing-subscriber = { version = "0.3.18", default-features = false }
|
||||
url = { version = "2.5.0", 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 }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
|
1
Makefile
1
Makefile
|
@ -11,6 +11,7 @@ format:
|
|||
|
||||
.PHONY: entities
|
||||
entities:
|
||||
rm --recursive --force ./packages/backend/built
|
||||
pnpm --filter=backend run build:debug
|
||||
pnpm run migrate
|
||||
$(MAKE) -C ./packages/backend-rs regenerate-entities
|
||||
|
|
|
@ -5,6 +5,6 @@ These are the extensions to ActivityPub that Firefish implements. This page uses
|
|||
## speakAsCat
|
||||
|
||||
- Compact IRI: `firefish:speakAsCat`
|
||||
- Canonical IRI: `https://firefish.dev/ns#speakascat`
|
||||
- Canonical IRI: `https://firefish.dev/ns#speakAsCat`
|
||||
|
||||
Used on actors to indicate that they not only identify as a cat, but also want to have their text be transformed to speak like one, expressed as a boolean value. If this property is set to true, displaying the actor’s posts will make them speak with “nya” instead of “na” and other cat-related text mannerisms. Used in combination with [misskey:isCat](https://misskey-hub.net/ns/#iscat).
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
Breaking changes are indicated by the :warning: icon.
|
||||
|
||||
## Unreleased
|
||||
|
||||
- `GET` request is now allowed for the `latest-version` endpoint.
|
||||
|
||||
## v20240523
|
||||
|
||||
- Added `scheduledAt` optional parameter to `notes/create` (!10789)
|
||||
|
|
|
@ -5,6 +5,16 @@ Critical security updates are indicated by the :warning: icon.
|
|||
- Server administrators should check [notice-for-admins.md](./notice-for-admins.md) as well.
|
||||
- Third-party client/bot developers may want to check [api-change.md](./api-change.md) as well.
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Add the ability to share posts via QR code
|
||||
- Update the API document page (`/api-doc`)
|
||||
- Fix bugs
|
||||
|
||||
## [v20240601](https://firefish.dev/firefish/firefish/-/merge_requests/10943/commits)
|
||||
|
||||
- Fix bugs
|
||||
|
||||
## [v20240523](https://firefish.dev/firefish/firefish/-/merge_requests/10898/commits)
|
||||
|
||||
- Add scheduled posts
|
||||
|
|
|
@ -4,9 +4,9 @@ Firefish depends on the following software.
|
|||
|
||||
## Runtime dependencies
|
||||
|
||||
- At least [NodeJS](https://nodejs.org/en/) v18.19.0 (v20/v21 recommended)
|
||||
- At least [NodeJS](https://nodejs.org/en/) v18.19.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
|
||||
- At least [Redis](https://redis.io/) v7 or [Valkey](https://valkey.io/) v7
|
||||
- Web Proxy (one of the following)
|
||||
- Caddy (recommended)
|
||||
- Nginx (recommended)
|
||||
|
@ -15,7 +15,7 @@ Firefish depends on the following software.
|
|||
- Caching server (**optional**, one of the following)
|
||||
- [DragonflyDB](https://www.dragonflydb.io/)
|
||||
- [KeyDB](https://keydb.dev/)
|
||||
- Another [Redis](https://redis.io/) server
|
||||
- Another [Redis](https://redis.io/) / [Valkey](https://valkey.io/) server
|
||||
|
||||
## Build dependencies
|
||||
|
||||
|
@ -30,8 +30,6 @@ This document shows an example procedure for installing these dependencies and F
|
|||
|
||||
If you want to use the pre-built container image, please refer to [`install-container.md`](./install-container.md).
|
||||
|
||||
If you do not prepare your environment as document, be sure to meet the minimum dependencies given at the bottom of the page.
|
||||
|
||||
Make sure that you can use the `sudo` command before proceeding.
|
||||
|
||||
## 1. Install dependencies
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
You can skip intermediate versions when upgrading from an old version, but please read the notices and follow the instructions for each intermediate version before [upgrading](./upgrade.md).
|
||||
|
||||
## Unreleased
|
||||
## v20240601
|
||||
|
||||
### For systemd/pm2 users
|
||||
|
||||
|
@ -32,7 +32,7 @@ Therefore, we have contributed to napi-rs to add support for `DateTime<FixedOffs
|
|||
|
||||
### For systemd/pm2 users
|
||||
|
||||
There is a bug where `pnpm install --frozen-lockfile` may fail on Linux 6.9.x ([GitHub issue](<https://github.com/nodejs/node/issues/53051>)).
|
||||
There is a bug where `pnpm install --frozen-lockfile` may fail on Linux 6.9.0, 6.9.1, and 6.9.2 ([GitHub issue](<https://github.com/nodejs/node/issues/53051>)).
|
||||
|
||||
To check your Linux kernel version, run:
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ lists: "Listen"
|
|||
noLists: "Du hast keine Listen angelegt"
|
||||
note: "Beitrag"
|
||||
notes: "Beiträge"
|
||||
following: "Folgen"
|
||||
following: "Folgend"
|
||||
followers: "Folgen mir"
|
||||
followsYou: "Folgt dir"
|
||||
createList: "Liste erstellen"
|
||||
|
@ -95,7 +95,7 @@ youShouldUpgradeClient: "Bitte aktualisiere diese Seite, um eine neuere Version
|
|||
Clients zu verwenden."
|
||||
enterListName: "Gib einen Namen für die Liste ein"
|
||||
privacy: "Privatsphäre"
|
||||
makeFollowManuallyApprove: "Folgeanfragen bedürfen der Genehmigung"
|
||||
makeFollowManuallyApprove: "Folgeanfragen müssen akzeptiert werden"
|
||||
defaultNoteVisibility: "Standard-Sichtbarkeit"
|
||||
follow: "Folgen"
|
||||
followRequest: "Follow anfragen"
|
||||
|
@ -430,7 +430,7 @@ securityKeyName: "Schlüsselname"
|
|||
registerSecurityKey: "Sicherheitsschlüssel registrieren"
|
||||
lastUsed: "Zuletzt benutzt"
|
||||
unregister: "Deaktivieren"
|
||||
passwordLessLogin: "Passwortloses Anmelden einrichten"
|
||||
passwordLessLogin: "Passwortloses Anmelden"
|
||||
resetPassword: "Passwort zurücksetzen"
|
||||
newPasswordIs: "Das neue Passwort ist „{password}“"
|
||||
reduceUiAnimation: "Animationen der Benutzeroberfläche reduzieren"
|
||||
|
@ -865,7 +865,7 @@ customCss: "Benutzerdefiniertes CSS"
|
|||
customCssWarn: "Verwende diese Einstellung nur, wenn du weißt, was sie tut. Ungültige
|
||||
Eingaben können dazu führen, dass der Client nicht mehr normal funktioniert."
|
||||
global: "Global"
|
||||
squareAvatars: "Profilbilder quadratisch anzeigen"
|
||||
squareAvatars: "Profilbilder für Accounts ohne Katzenohren quadratisch anzeigen"
|
||||
sent: "Gesendet"
|
||||
received: "Erhalten"
|
||||
searchResult: "Suchergebnisse"
|
||||
|
@ -948,7 +948,7 @@ check: "Überprüfe"
|
|||
driveCapOverrideLabel: "Die Cloud-Drive-Kapazität dieses Nutzers verändern"
|
||||
driveCapOverrideCaption: "Gib einen Wert von 0 oder weniger ein, um die Kapazität
|
||||
auf den Standard zurückzusetzen."
|
||||
requireAdminForView: "Du musst dich mit einem Administratorkonto anmelden um dies
|
||||
requireAdminForView: "Du musst dich mit einem Administratorkonto anmelden, um dies
|
||||
zu sehen."
|
||||
isSystemAccount: "Ein Nutzerkonto, dass durch das System erstellt und automatisch
|
||||
kontrolliert wird. Jede Anpassung, Veränderung oder Löschung dieses Nutzerkontos,
|
||||
|
@ -1241,6 +1241,7 @@ _wordMute:
|
|||
muteLangsDescription2: Sprachcode verwenden, z.B. en, fr, ja, zh..
|
||||
lang: Sprache
|
||||
langDescription: Beiträge in der angegebenen Sprache in der Timeline ausblenden.
|
||||
mutePatterns: Gedämpfte Muster
|
||||
_instanceMute:
|
||||
instanceMuteDescription: "Schaltet alle Beiträge/Boosts stumm, die von den gelisteten
|
||||
Servern stammen, inklusive Antworten von Nutzern an einen Nutzer eines stummgeschalteten
|
||||
|
@ -1332,7 +1333,7 @@ _sfx:
|
|||
channel: "Kanalbenachrichtigung"
|
||||
_ago:
|
||||
future: "Zukunft"
|
||||
justNow: "Gerade eben"
|
||||
justNow: "gerade eben"
|
||||
secondsAgo: "vor {n} s"
|
||||
minutesAgo: "vor {n} min"
|
||||
hoursAgo: "vor {n} h"
|
||||
|
@ -1931,6 +1932,7 @@ _notification:
|
|||
voted: haben bei deiner Umfrage abgestimmt
|
||||
reacted: hat auf deinen Beitrag reagiert
|
||||
renoted: hat deinen Beitrag geteilt
|
||||
andCountUsers: und {count} mehr Nutzer {acted}
|
||||
_deck:
|
||||
alwaysShowMainColumn: "Hauptspalte immer zeigen"
|
||||
columnAlign: "Spaltenausrichtung"
|
||||
|
@ -1979,8 +1981,8 @@ flagSpeakAsCatDescription: Deine Beiträge werden im Katzenmodus nyanisiert
|
|||
hiddenTags: Versteckte Hashtags
|
||||
antennaInstancesDescription: Geben sie einen Server-Namen pro Zeile ein
|
||||
secureModeInfo: Bei Anfragen an andere Server nicht ohne Nachweis zurücksenden.
|
||||
renoteMute: Boosts stummschalten
|
||||
renoteUnmute: Stummschaltung von Boosts aufheben
|
||||
renoteMute: Boosts in Timelines stummschalten
|
||||
renoteUnmute: Stummschaltung von Boosts in der Timeline aufheben
|
||||
noInstances: Keine Server gefunden
|
||||
privateModeInfo: Wenn diese Option aktiviert ist, können nur als vertrauenswürdig
|
||||
eingestufte Server mit diesem Server föderieren. Alle Beiträge werden für die Öffentlichkeit
|
||||
|
@ -2019,9 +2021,8 @@ moveAccountDescription: 'Dieser Vorgang kann nicht rückgängig gemacht werden!
|
|||
wie folgt ein: @name@server.xyz'
|
||||
sendPushNotificationReadMessage: Löschung der Push-Benachrichtigungen sobald die entsprechenden
|
||||
Benachrichtigungen oder Nachrichten gelesen wurden
|
||||
signupsDisabled: Derzeit sind keine Anmeldungen auf diesem Server möglich! Anmeldungen
|
||||
auf anderen Servern sind jedoch möglich! Wenn Sie einen Einladungscode für diesen
|
||||
Server haben, geben Sie ihn bitte unten ein.
|
||||
signupsDisabled: Derzeit sind keine Anmeldungen auf diesem Server möglich. Wenn Sie
|
||||
einen Einladungscode für diesen Server haben, geben Sie ihn bitte unten ein.
|
||||
swipeOnDesktop: Am Desktop PC das Wischen wie bei mobilen Geräten zulassen
|
||||
enterSendsMessage: Drücken sie zum Senden des Beitrages die Eingabetaste (Strg-Taste
|
||||
ausgeschaltet)
|
||||
|
@ -2212,3 +2213,129 @@ quotes: Zitate
|
|||
moreUrlsDescription: "Die Seiten, welche angepinnt werde sollen, im Hilfe-Menü in
|
||||
der unteren linken Ecke in folgender Notation angeben:\n\"Anzeigename\": https://example.com/"
|
||||
toQuote: Zitat
|
||||
releaseToReload: Loslassen, um neu zu laden
|
||||
pullDownToReload: Herunterziehen zum Aktualisieren
|
||||
antennaLimit: Die maximale Anzahl von Antennen, die jeder Nutzer erstellen kann
|
||||
toEdit: Bearbeiten
|
||||
squareCatAvatars: Profilbilder für Accounts mit Katzenohren quadratisch anzeigen
|
||||
moderationNote: Moderationsnotiz
|
||||
ipFirstAcknowledged: Das Datum des ersten Erwerbs der IP Adresse
|
||||
driveCapacityOverride: Benutzerdefinierte Speicherkapazität
|
||||
searchWordsDescription: "Hier den Suchbegriff für Beiträge eingeben. Mit einem Leerzeichen
|
||||
getrennte Begriffe werden in einer UND Suche gesucht, um eine ODER Suche auszuführen
|
||||
'OR' (ohne Anführungszeichen) zwischen die Begriffe schreiben.\nZum Beispiel findet
|
||||
die Suche nach \"Morgen Nacht\" Beiträge, die sowohl \"Morgen, als auch \"Nacht\"\
|
||||
\ enthalten. Die Suchanfrage \"Morgen OR Nacht\" findet Beiträge, die entweder \"\
|
||||
Morgen\" oder \"Nacht\" (oder beides) enthalten.\nDie AND und OR Suche ist zudem
|
||||
kombinierbar, z.B. so: \"(Morgen OR Nacht) Eule)\".\nUm nach einer Sequenz von Wörtern
|
||||
(z.B. einem Satz) zu suchen, muss die gesamte Wortsequenz in Anführungszeichen stehen.
|
||||
Beispiel: \"Nachrichten von heute\"\n\nUm zu einem bestimmten Profil oder Beitrag
|
||||
zu gelangen, muss die ID oder URL (Webadresse) eingegeben und der Suchknopf gedrückt
|
||||
werden. Ansonsten wird nach Beiträgen gesucht, die die ID oder URL wörtlich enthalten."
|
||||
useCdnDescription: Einige statische Ressourcen, wie einen Twemoji, vom JSDelivr CDN
|
||||
anstatt von diesem Firefish server laden.
|
||||
suggested: Vorgeschlagen
|
||||
preventMisclick: Schutz vor versehentlichen Clicks
|
||||
replaceWidgetsButtonWithReloadButton: Widget-Knopf durch Aktualisierungs-Knopf ersetzen
|
||||
hideFollowButtons: Folgen-Knopf in einer versehentlich clickbaren Position verstecken
|
||||
forMobile: Mobil
|
||||
privateDescription: Nur für Sie sichtbar machen
|
||||
makePrivate: Als privat markieren
|
||||
searchUsers: Erstellt von (optional)
|
||||
searchWords: Suchbegriffe / ID oder URL als Suchanfrage
|
||||
searchCwAndAlt: Inhaltswarnungen und Beschreibungen von Dateien einbeziehen
|
||||
searchUsersDescription: "Um nach Beiträgen eines bestimmten Nutzers/ Servers zu suchen,
|
||||
einfach die ID (@Benutzer@beispiel.de, or @Benutzer für einen lokalen Benutzer)
|
||||
oder Webadresse (beispiel.de) eingeben.\n\nDie Suche \"me\" (ohne Anführungszeichen)
|
||||
findet alle Ihre Beiträge (auch nicht-gelistete, direkte, geheime Beiträge und Beiträge,
|
||||
die nur für Follower sichtbar sind).\n\nDie Suche \"local\" (ohne Anführungszeichen)
|
||||
sorgt dafür, dass nur Beiträge von diesem Server angezeigt werden."
|
||||
publishTimelines: Timelines für Besucher veröffentlichen
|
||||
publishTimelinesDescription: Falls konfiguriert, werden die lokale und globale Timeline
|
||||
auf {url} auch ohne Anmeldung angezeigt.
|
||||
showNoAltTextWarning: Eine Warnung beim Hochladen von Dateien ohne Beschreibung anzeigen
|
||||
_emojiModPerm:
|
||||
add: Hinzufügen
|
||||
full: Alles erlauben
|
||||
unauthorized: Kein(e)
|
||||
mod: Hinzufügen und bearbeiten
|
||||
messagingUnencryptedInfo: Unterhaltungen auf Firefish sind nicht Ende-zu-Ende verschlüsselt.
|
||||
Teilen Sie keine sensiblen Informationen über Firefish.
|
||||
autocorrectNoteLanguage: Eine Warnung anzeigen, wenn die Beitragssprache nicht mit
|
||||
der automatisch ermittelten Sprache übereinstimmt
|
||||
emojiModPerm: Berechtigung, personalisierte Emojis zu verwalten
|
||||
emojiModPermDescription: "Hinzufügen: Erlauben Sie diesem Benutzer, neue benutzerdefinierte
|
||||
Emojis hinzuzufügen und Tag/Kategorie/Lizenz für neu hinzugefügte benutzerdefinierte
|
||||
Emojis einzustellen.\nHinzufügen und Bearbeiten: \"Hinzufügen\" Berechtigung + Erlauben
|
||||
Sie diesem Benutzer, den Namen/die Kategorie/Tag/die Lizenz der vorhandenen benutzerdefinierten
|
||||
Emojis zu bearbeiten.\nAlles erlauben: \"Hinzufügen und Bearbeiten\" Berechtigung
|
||||
+ Erlauben Sie diesem Benutzer, bestehende benutzerdefinierte Emojis zu löschen."
|
||||
reloading: Aktualisiert
|
||||
markLocalFilesNsfwByDefault: Standardmäßig alle neuen lokalen Dateien als sensibel
|
||||
markieren
|
||||
markLocalFilesNsfwByDefaultDescription: Unabhäning von dieser Einstellung lässt sich
|
||||
eine NSFW-Markierung entfernen. Bereits existierende Dateien sind nicht betroffen.
|
||||
noLanguage: Keine Sprache
|
||||
showBigPostButton: Anzeigen eines großen Knopfes zum Teilen des Beitrags im Beitragsformular
|
||||
private: Privat
|
||||
searchRange: Veröffentlicht zwischen (optional)
|
||||
searchPostsWithFiles: Nur Beiträge mit Dateien
|
||||
noAltTextWarning: Einige der angehängten Dateien haben keine Beschreibung. Haben Sie
|
||||
vergessen, diese zu schreiben?
|
||||
toReply: Antworten
|
||||
toPost: Beitrag teilen
|
||||
sentFollowRequests: Gesendete Follow-Anfragen
|
||||
replyMute: Antworten in Timelines stummschalten
|
||||
replyUnmute: Stummschaltung von Antworten in Timelines aufheben
|
||||
noSentFollowRequests: Keine gesendeten Follow-Anfragen
|
||||
postSearch: Beitragssuche auf diesem Server
|
||||
enablePullToRefresh: '"Herunterziehen um zu aktualisieren" aktivieren'
|
||||
pullToRefreshThreshold: Benötigte heruntergezogene Distanz, um zu Aktualisieren
|
||||
showAddFileDescriptionAtFirstPost: Öffne automatisch ein Eingabefeld, um fehlende
|
||||
Dateibeschreibungen beim Hochladen zu ergänßen
|
||||
searchRangeDescription: "Um eine Zeitspanne zu filtern, geben Sie diese in diesem
|
||||
Format an: 20220615-20231031 (YYYYMMTT)\n\nDas Auslassen der Jahreszahl (z.B.: 0615-1031
|
||||
oder 20220615-1031) wird automatisch wie die aktuelle Jahreszahl interpretiert.\n
|
||||
\nZudem können das Anfangs- oder Enddatum ausgelassen werden. Zum Beispiel gibt
|
||||
-1031 an, nach Beiträgen vor dem 31.10 dieses Jahres zu suchen. Umgekehrt führt
|
||||
20220615- zu einer Suche nach allen Beiträgen nach dem 15.6.2022."
|
||||
incorrectLanguageWarning: "Es sieht so aus, als wäre ihr Beitrag auf {detected}, aber
|
||||
Sie haben {current} ausgewählt.\nMöchten Sie stattdessen die Sprache zu {detected}
|
||||
ändern?"
|
||||
noteEditHistory: Bearbeitungsgeschichte des Beitrags
|
||||
_later:
|
||||
justNow: gerade eben
|
||||
secondsAgo: in {n}s
|
||||
minutesAgo: in {n}min
|
||||
hoursAgo: in {n}h
|
||||
daysAgo: in {n}d
|
||||
weeksAgo: in {n} Woche(n)
|
||||
monthsAgo: in {n} Monat(en)
|
||||
yearsAgo: in {n} Jahr(en)
|
||||
future: zukünftig
|
||||
scheduledPost: Veröffentlichungszeit manuell festlegen
|
||||
scheduledDate: Geplantes Datum
|
||||
mergeRenotesInTimeline: Mehrere Boosts eines Beitrags gruppieren
|
||||
mergeThreadInTimeline: In der Timeline mehrere Beiträge im gleichen Thread zusammenlegen
|
||||
cannotEditVisibility: Die Sichtbarkeit lässt sich nicht einstellen
|
||||
useThisAccountConfirm: Mit diesem Benutzerkonto fortfahren?
|
||||
inputAccountId: Bitte gib dein Benutzerkonto an (z.B. @firefish@info.firefish.dev)
|
||||
remoteFollow: Folgen (fremde Instanz)
|
||||
foldNotification: Ähnliche Benachrichtigungen gruppieren
|
||||
i18nServerInfo: Neue Clients nutzen standardmäßig {language}.
|
||||
i18nServerChange: Stattdessen {language} benutzen.
|
||||
i18nServerSet: Für neue Clients {language} benutzen.
|
||||
getQrCode: QR Code anzeigen
|
||||
useCdn: Ressourcen von einem CDN laden
|
||||
copyRemoteFollowUrl: URL zum Folgen auf einer fremden Instanz kopieren
|
||||
showPreviewByDefault: Standardmäßig Vorschau in Beitragsform anzeigen
|
||||
replaceChatButtonWithAccountButton: Unterhaltungen-Knopf durch Knopf zum Wechseln
|
||||
des Benutzerkontos ersetzen
|
||||
searchEngine: Verwendete Suchmaschine in der Suchleiste MFM
|
||||
makePrivateConfirm: Diese Operation sendet eine Löschungsanfrage an fremde Server
|
||||
und ändert die Sichtbarkeit zu 'privat'. Fortfahren?
|
||||
enableTimelineStreaming: Timelines automatisch aktualisieren
|
||||
scheduledPostAt: Der Beitrag wird {time} gesendet
|
||||
cancelScheduledPost: Zeitplan entfernen
|
||||
media: Medien
|
||||
slashQuote: Kettenzitat
|
||||
|
|
|
@ -1236,8 +1236,8 @@ publishTimelinesDescription: "If enabled, the Local and Global timelines will be
|
|||
on {url} even when signed out."
|
||||
noAltTextWarning: "Some attached file(s) have no description. Did you forget to write?"
|
||||
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"
|
||||
showAddFileDescriptionAtFirstPost: "Automatically open a form to write a description
|
||||
when you attempt to post files without a description"
|
||||
|
||||
_emojiModPerm:
|
||||
unauthorized: "None"
|
||||
|
|
|
@ -1073,7 +1073,7 @@ _aboutFirefish:
|
|||
source: "Código fuente"
|
||||
translation: "Traducir Firefish"
|
||||
donate: "Donar a Firefish"
|
||||
pleaseDonateToFirefish: Por favor considera donar a Firefish para apollar su desarrollo.
|
||||
pleaseDonateToFirefish: Por favor considera donar a Firefish para apoyar su desarrollo.
|
||||
donateHost: Dona a {host}
|
||||
donateTitle: ¿Te gusta Firefish?
|
||||
pleaseDonateToHost: También considera donar a tu propio servidor , {host}, para
|
||||
|
|
|
@ -244,3 +244,11 @@ enableEmojiReactions: Ativar reações com emoji
|
|||
attachCancel: Remover anexo
|
||||
flagShowTimelineReplies: Mostrar respostas na linha do tempo
|
||||
addAccount: Adicionar conta
|
||||
toReply: Responder
|
||||
sentFollowRequests: Enviou solicitações para seguir
|
||||
toPost: Postar
|
||||
renotes: Impulsionamentos
|
||||
renote: Impulsionar
|
||||
unrenote: Retirar o impulsionamento
|
||||
renoted: Impulsionado.
|
||||
replies: Respostas
|
||||
|
|
|
@ -1950,7 +1950,7 @@ noteId: 帖子 ID
|
|||
moveFrom: 从旧账号迁移至此账号
|
||||
defaultReaction: 发出和收到帖子的默认表情符号反应
|
||||
sendModMail: 发送管理通知
|
||||
moderationNote: "管理笔记"
|
||||
moderationNote: "管理员备注"
|
||||
ipFirstAcknowledged: "首次获取此 IP 地址的日期"
|
||||
driveCapacityOverride: "网盘容量变更"
|
||||
isLocked: 该账号设置了关注请求
|
||||
|
|
18
package.json
18
package.json
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"name": "firefish",
|
||||
"version": "20240523",
|
||||
"version": "20240601",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://firefish.dev/firefish/firefish.git"
|
||||
},
|
||||
"packageManager": "pnpm@9.1.2",
|
||||
"packageManager": "pnpm@9.2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"rebuild": "pnpm run clean && pnpm run build",
|
||||
|
@ -41,14 +41,14 @@
|
|||
"js-yaml": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.7.3",
|
||||
"@biomejs/cli-darwin-arm64": "1.7.3",
|
||||
"@biomejs/cli-darwin-x64": "1.7.3",
|
||||
"@biomejs/cli-linux-arm64": "1.7.3",
|
||||
"@biomejs/cli-linux-x64": "1.7.3",
|
||||
"@types/node": "20.12.12",
|
||||
"@biomejs/biome": "1.8.0",
|
||||
"@biomejs/cli-darwin-arm64": "1.8.0",
|
||||
"@biomejs/cli-darwin-x64": "1.8.0",
|
||||
"@biomejs/cli-linux-arm64": "1.8.0",
|
||||
"@biomejs/cli-linux-x64": "1.8.0",
|
||||
"@types/node": "20.14.2",
|
||||
"execa": "9.1.0",
|
||||
"pnpm": "9.1.2",
|
||||
"pnpm": "9.2.0",
|
||||
"typescript": "5.4.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[*.rs]
|
||||
indent_style = space
|
||||
indent_size = 4
|
|
@ -14,43 +14,43 @@ crate-type = ["cdylib", "lib"]
|
|||
[dependencies]
|
||||
macro-rs = { workspace = true }
|
||||
|
||||
napi = { workspace = true, optional = true, default-features = false, features = ["napi9", "tokio_rt", "chrono_date", "serde-json"] }
|
||||
napi = { workspace = true, optional = true, features = ["chrono_date", "napi4", "serde-json", "tokio_rt"] }
|
||||
napi-derive = { workspace = true, optional = true }
|
||||
|
||||
argon2 = { workspace = true, features = ["std"] }
|
||||
async-trait = { workspace = true }
|
||||
basen = { workspace = true }
|
||||
bb8 = { workspace = true }
|
||||
bcrypt = { workspace = true }
|
||||
bcrypt = { workspace = true, features = ["std"] }
|
||||
chrono = { workspace = true }
|
||||
cuid2 = { workspace = true }
|
||||
emojis = { workspace = true }
|
||||
idna = { workspace = true }
|
||||
image = { workspace = true }
|
||||
isahc = { workspace = true }
|
||||
image = { workspace = true, features = ["avif", "bmp", "gif", "ico", "jpeg", "png", "tiff", "webp"] }
|
||||
isahc = { workspace = true, features = ["http2", "text-decoding"] }
|
||||
nom-exif = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
openssl = { workspace = true, features = ["vendored"] }
|
||||
rand = { workspace = true }
|
||||
redis = { workspace = true, default-features = false, features = ["streams", "tokio-comp"] }
|
||||
redis = { workspace = true, features = ["streams", "tokio-comp"] }
|
||||
regex = { workspace = true }
|
||||
rmp-serde = { workspace = true }
|
||||
sea-orm = { workspace = true, features = ["sqlx-postgres", "runtime-tokio-rustls"] }
|
||||
sea-orm = { workspace = true, features = ["macros", "runtime-tokio-rustls", "sqlx-postgres", "with-chrono", "with-json"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
serde_yaml = { workspace = true }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
sysinfo = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tokio = { workspace = true, features = ["fs", "io-std", "io-util", "macros", "process", "rt-multi-thread", "signal", "sync", "time"] }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, features = ["ansi"] }
|
||||
url = { workspace = true }
|
||||
urlencoding = { workspace = true }
|
||||
web-push = { workspace = true }
|
||||
web-push = { workspace = true, features = ["isahc-client"] }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = { workspace = true }
|
||||
pretty_assertions = { workspace = true, features = ["std"] }
|
||||
tokio-test = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
|
|
331
packages/backend-rs/index.d.ts
vendored
331
packages/backend-rs/index.d.ts
vendored
|
@ -11,8 +11,9 @@ export const USER_ONLINE_THRESHOLD: number
|
|||
export const USER_ACTIVE_THRESHOLD: number
|
||||
/**
|
||||
* List of file types allowed to be viewed directly in the browser
|
||||
*
|
||||
* Anything not included here will be responded as application/octet-stream
|
||||
* SVG is not allowed because it generates XSS <- we need to fix this and later allow it to be viewed directly
|
||||
* SVG is not allowed because it generates XSS (TODO: fix this and later allow it to be viewed directly)
|
||||
* * <https://github.com/sindresorhus/file-type/blob/main/supported.js>
|
||||
* * <https://github.com/sindresorhus/file-type/blob/main/core.js>
|
||||
* * <https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers>
|
||||
|
@ -28,6 +29,20 @@ export interface EnvConfig {
|
|||
slow: boolean
|
||||
}
|
||||
export function loadEnv(): EnvConfig
|
||||
export function fetchMeta(): Promise<Meta>
|
||||
export function updateMetaCache(): Promise<void>
|
||||
export interface PugArgs {
|
||||
img: string | null
|
||||
title: string
|
||||
instanceName: string
|
||||
desc: string | null
|
||||
icon: string | null
|
||||
splashIcon: string | null
|
||||
themeColor: string | null
|
||||
randomMotd: string
|
||||
privateMode: boolean | null
|
||||
}
|
||||
export function metaToPugArgs(meta: Meta): PugArgs
|
||||
export interface ServerConfig {
|
||||
url: string
|
||||
port: number
|
||||
|
@ -207,48 +222,206 @@ export interface Acct {
|
|||
}
|
||||
export function stringToAcct(acct: string): Acct
|
||||
export function acctToString(acct: Acct): string
|
||||
/** Fetches and returns the NodeInfo (version 2.0) of a remote server. */
|
||||
export function fetchNodeinfo(host: string): Promise<Nodeinfo>
|
||||
export function nodeinfo_2_1(): Promise<any>
|
||||
export function nodeinfo_2_0(): Promise<any>
|
||||
/** NodeInfo schema version 2.0. <https://nodeinfo.diaspora.software/docson/index.html#/ns/schema/2.0> */
|
||||
export interface Nodeinfo {
|
||||
/** The schema version, must be 2.0. */
|
||||
version: string
|
||||
/** Metadata about server software in use. */
|
||||
software: Software20
|
||||
/** The protocols supported on this server. */
|
||||
protocols: Array<Protocol>
|
||||
/** The third party sites this server can connect to via their application API. */
|
||||
services: Services
|
||||
/** Whether this server allows open self-registration. */
|
||||
openRegistrations: boolean
|
||||
/** Usage statistics for this server. */
|
||||
usage: Usage
|
||||
/** Free form key value pairs for software specific values. Clients should not rely on any specific key present. */
|
||||
metadata: Record<string, any>
|
||||
}
|
||||
/** Metadata about server software in use (version 2.0). */
|
||||
export interface Software20 {
|
||||
/** The canonical name of this server software. */
|
||||
name: string
|
||||
/** The version of this server software. */
|
||||
version: string
|
||||
}
|
||||
export enum Protocol {
|
||||
Activitypub = 'activitypub',
|
||||
Buddycloud = 'buddycloud',
|
||||
Dfrn = 'dfrn',
|
||||
Diaspora = 'diaspora',
|
||||
Libertree = 'libertree',
|
||||
Ostatus = 'ostatus',
|
||||
Pumpio = 'pumpio',
|
||||
Tent = 'tent',
|
||||
Xmpp = 'xmpp',
|
||||
Zot = 'zot'
|
||||
}
|
||||
/** The third party sites this server can connect to via their application API. */
|
||||
export interface Services {
|
||||
/** The third party sites this server can retrieve messages from for combined display with regular traffic. */
|
||||
inbound: Array<Inbound>
|
||||
/** The third party sites this server can publish messages to on the behalf of a user. */
|
||||
outbound: Array<Outbound>
|
||||
}
|
||||
/** The third party sites this server can retrieve messages from for combined display with regular traffic. */
|
||||
export enum Inbound {
|
||||
Atom1 = 'atom1',
|
||||
Gnusocial = 'gnusocial',
|
||||
Imap = 'imap',
|
||||
Pnut = 'pnut',
|
||||
Pop3 = 'pop3',
|
||||
Pumpio = 'pumpio',
|
||||
Rss2 = 'rss2',
|
||||
Twitter = 'twitter'
|
||||
}
|
||||
/** The third party sites this server can publish messages to on the behalf of a user. */
|
||||
export enum Outbound {
|
||||
Atom1 = 'atom1',
|
||||
Blogger = 'blogger',
|
||||
Buddycloud = 'buddycloud',
|
||||
Diaspora = 'diaspora',
|
||||
Dreamwidth = 'dreamwidth',
|
||||
Drupal = 'drupal',
|
||||
Facebook = 'facebook',
|
||||
Friendica = 'friendica',
|
||||
Gnusocial = 'gnusocial',
|
||||
Google = 'google',
|
||||
Insanejournal = 'insanejournal',
|
||||
Libertree = 'libertree',
|
||||
Linkedin = 'linkedin',
|
||||
Livejournal = 'livejournal',
|
||||
Mediagoblin = 'mediagoblin',
|
||||
Myspace = 'myspace',
|
||||
Pinterest = 'pinterest',
|
||||
Pnut = 'pnut',
|
||||
Posterous = 'posterous',
|
||||
Pumpio = 'pumpio',
|
||||
Redmatrix = 'redmatrix',
|
||||
Rss2 = 'rss2',
|
||||
Smtp = 'smtp',
|
||||
Tent = 'tent',
|
||||
Tumblr = 'tumblr',
|
||||
Twitter = 'twitter',
|
||||
Wordpress = 'wordpress',
|
||||
Xmpp = 'xmpp'
|
||||
}
|
||||
/** Usage statistics for this server. */
|
||||
export interface Usage {
|
||||
users: Users
|
||||
localPosts: number | null
|
||||
localComments: number | null
|
||||
}
|
||||
/** statistics about the users of this server. */
|
||||
export interface Users {
|
||||
total: number | null
|
||||
activeHalfyear: number | null
|
||||
activeMonth: number | null
|
||||
}
|
||||
/** Prints the greeting message and the Firefish version to stdout. */
|
||||
export function greet(): void
|
||||
/** Initializes the [tracing] logger. */
|
||||
export function initializeRustLogger(): void
|
||||
/** Prints the server hardware information as the server info log. */
|
||||
export function showServerInfo(): void
|
||||
/**
|
||||
* Checks if a server is blocked.
|
||||
*
|
||||
* ## Argument
|
||||
* # Argument
|
||||
* `host` - punycoded instance host
|
||||
*
|
||||
* # Example
|
||||
* ```no_run
|
||||
* # use backend_rs::misc::check_server_block::is_blocked_server;
|
||||
* # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
* assert_eq!(true, is_blocked_server("blocked.com").await?);
|
||||
* assert_eq!(false, is_blocked_server("not-blocked.com").await?);
|
||||
* assert_eq!(true, is_blocked_server("subdomain.of.blocked.com").await?);
|
||||
* assert_eq!(true, is_blocked_server("xn--l8jegik.blocked.com").await?);
|
||||
* # Ok(())
|
||||
* # }
|
||||
* ```
|
||||
*/
|
||||
export function isBlockedServer(host: string): Promise<boolean>
|
||||
/**
|
||||
* Checks if a server is silenced.
|
||||
*
|
||||
* ## Argument
|
||||
* # Argument
|
||||
* `host` - punycoded instance host
|
||||
*
|
||||
* # Example
|
||||
* ```no_run
|
||||
* # use backend_rs::misc::check_server_block::is_silenced_server;
|
||||
* # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
* assert_eq!(true, is_silenced_server("silenced.com").await?);
|
||||
* assert_eq!(false, is_silenced_server("not-silenced.com").await?);
|
||||
* assert_eq!(true, is_silenced_server("subdomain.of.silenced.com").await?);
|
||||
* assert_eq!(true, is_silenced_server("xn--l8jegik.silenced.com").await?);
|
||||
* # Ok(())
|
||||
* # }
|
||||
* ```
|
||||
*/
|
||||
export function isSilencedServer(host: string): Promise<boolean>
|
||||
/**
|
||||
* Checks if a server is allowlisted.
|
||||
* Returns `Ok(true)` if private mode is disabled.
|
||||
*
|
||||
* ## Argument
|
||||
* # Argument
|
||||
* `host` - punycoded instance host
|
||||
*
|
||||
* # Example
|
||||
* ```no_run
|
||||
* # use backend_rs::misc::check_server_block::is_allowed_server;
|
||||
* # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
* assert_eq!(true, is_allowed_server("allowed.com").await?);
|
||||
* assert_eq!(false, is_allowed_server("not-allowed.com").await?);
|
||||
* assert_eq!(false, is_allowed_server("subdomain.of.allowed.com").await?);
|
||||
* assert_eq!(false, is_allowed_server("xn--l8jegik.allowed.com").await?);
|
||||
* # Ok(())
|
||||
* # }
|
||||
* ```
|
||||
*/
|
||||
export function isAllowedServer(host: string): Promise<boolean>
|
||||
export function checkWordMute(note: NoteLike, mutedWords: Array<string>, mutedPatterns: Array<string>): Promise<boolean>
|
||||
/**
|
||||
* Returns whether `note` should be hard-muted.
|
||||
*
|
||||
* More specifically, this function returns `Ok(true)`
|
||||
* if and only if one or more of these conditions are met:
|
||||
*
|
||||
* * the note (text or CW) contains any of the words/patterns
|
||||
* * the "parent" note(s) (reply, quote) contain any of the words/patterns
|
||||
* * the alt text of the attached files contains any of the words/patterns
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `note` : [PartialNoteToElaborate] object
|
||||
* * `muted_words` : list of muted keyword lists (each array item is a space-separated keyword list that represents an AND condition)
|
||||
* * `muted_patterns` : list of JavaScript-style (e.g., `/foo/i`) regular expressions
|
||||
*/
|
||||
export function checkWordMute(note: PartialNoteToElaborate, mutedWords: Array<string>, mutedPatterns: Array<string>): Promise<boolean>
|
||||
export function getFullApAccount(username: string, host?: string | undefined | null): string
|
||||
export function isSelfHost(host?: string | undefined | null): boolean
|
||||
export function isSameOrigin(uri: string): boolean
|
||||
export function extractHost(uri: string): string
|
||||
export function toPuny(host: string): string
|
||||
export function isUnicodeEmoji(s: string): boolean
|
||||
/** Escapes `%` and `\` in the given string. */
|
||||
export function sqlLikeEscape(src: string): string
|
||||
/** Returns `true` if `src` does not contain suspicious characters like `%`. */
|
||||
export function safeForSql(src: string): boolean
|
||||
/** Convert milliseconds to a human readable string */
|
||||
/** Converts milliseconds to a human readable string. */
|
||||
export function formatMilliseconds(milliseconds: number): string
|
||||
export interface ImageSize {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
export function getImageSizeFromUrl(url: string): Promise<ImageSize>
|
||||
export interface NoteLikeForAllTexts {
|
||||
export interface PartialNoteToElaborate {
|
||||
fileIds: Array<string>
|
||||
userId: string
|
||||
text: string | null
|
||||
|
@ -256,34 +429,44 @@ export interface NoteLikeForAllTexts {
|
|||
renoteId: string | null
|
||||
replyId: string | null
|
||||
}
|
||||
export interface NoteLikeForGetNoteSummary {
|
||||
export interface PartialNoteToSummarize {
|
||||
fileIds: Array<string>
|
||||
text: string | null
|
||||
cw: string | null
|
||||
hasPoll: boolean
|
||||
}
|
||||
export function getNoteSummary(note: NoteLikeForGetNoteSummary): string
|
||||
export function getNoteSummary(note: PartialNoteToSummarize): string
|
||||
export function isQuote(note: Note): boolean
|
||||
export function isSafeUrl(url: string): boolean
|
||||
/** Returns the latest Firefish version. */
|
||||
export function latestVersion(): Promise<string>
|
||||
export function toMastodonId(firefishId: string): string | null
|
||||
export function fromMastodonId(mastodonId: string): string | null
|
||||
export function fetchMeta(useCache: boolean): Promise<Meta>
|
||||
export interface PugArgs {
|
||||
img: string | null
|
||||
title: string
|
||||
instanceName: string
|
||||
desc: string | null
|
||||
icon: string | null
|
||||
splashIcon: string | null
|
||||
themeColor: string | null
|
||||
randomMotd: string
|
||||
privateMode: boolean | null
|
||||
}
|
||||
export function metaToPugArgs(meta: Meta): PugArgs
|
||||
/**
|
||||
* Converts the given text into the cat language.
|
||||
*
|
||||
* refs:
|
||||
* * <https://misskey-hub.net/ns/#isCat>
|
||||
* * <https://firefish.dev/ns#speakAsCat>
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `text` : original text
|
||||
* * `lang` : language code (e.g., `Some("en")`, `Some("en-US")`, `Some("uk-UA")`, `None`)
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* ```
|
||||
* # use backend_rs::misc::nyaify::nyaify;
|
||||
* assert_eq!(nyaify("I'll take a nap.", Some("en")), "I'll take a nyap.");
|
||||
* ```
|
||||
*/
|
||||
export function nyaify(text: string, lang?: string | undefined | null): string
|
||||
/** Hashes the given password using [Argon2] algorithm. */
|
||||
export function hashPassword(password: string): string
|
||||
/** Checks whether the given password and hash match. */
|
||||
export function verifyPassword(password: string, hash: string): boolean
|
||||
/** Returns whether the [bcrypt] algorithm is used for the password hash. */
|
||||
export function isOldPasswordAlgorithm(hash: string): boolean
|
||||
export interface DecodedReaction {
|
||||
reaction: string
|
||||
|
@ -293,7 +476,7 @@ export interface DecodedReaction {
|
|||
export function decodeReaction(reaction: string): DecodedReaction
|
||||
export function countReactions(reactions: Record<string, number>): Record<string, number>
|
||||
export function toDbReaction(reaction?: string | undefined | null, host?: string | undefined | null): Promise<string>
|
||||
/** Delete all entries in the "attestation_challenge" table created at more than 5 minutes ago */
|
||||
/** Delete all entries in the [attestation_challenge] table created at more than 5 minutes ago */
|
||||
export function removeOldAttestationChallenges(): Promise<void>
|
||||
export interface Cpu {
|
||||
model: string
|
||||
|
@ -1174,106 +1357,6 @@ export interface Webhook {
|
|||
latestStatus: number | null
|
||||
}
|
||||
export function updateAntennasOnNewNote(note: Note, noteAuthor: Acct, noteMutedUsers: Array<string>): Promise<void>
|
||||
export function fetchNodeinfo(host: string): Promise<Nodeinfo>
|
||||
export function nodeinfo_2_1(): Promise<any>
|
||||
export function nodeinfo_2_0(): Promise<any>
|
||||
/** NodeInfo schema version 2.0. <https://nodeinfo.diaspora.software/docson/index.html#/ns/schema/2.0> */
|
||||
export interface Nodeinfo {
|
||||
/** The schema version, must be 2.0. */
|
||||
version: string
|
||||
/** Metadata about server software in use. */
|
||||
software: Software20
|
||||
/** The protocols supported on this server. */
|
||||
protocols: Array<Protocol>
|
||||
/** The third party sites this server can connect to via their application API. */
|
||||
services: Services
|
||||
/** Whether this server allows open self-registration. */
|
||||
openRegistrations: boolean
|
||||
/** Usage statistics for this server. */
|
||||
usage: Usage
|
||||
/** Free form key value pairs for software specific values. Clients should not rely on any specific key present. */
|
||||
metadata: Record<string, any>
|
||||
}
|
||||
/** Metadata about server software in use (version 2.0). */
|
||||
export interface Software20 {
|
||||
/** The canonical name of this server software. */
|
||||
name: string
|
||||
/** The version of this server software. */
|
||||
version: string
|
||||
}
|
||||
export enum Protocol {
|
||||
Activitypub = 'activitypub',
|
||||
Buddycloud = 'buddycloud',
|
||||
Dfrn = 'dfrn',
|
||||
Diaspora = 'diaspora',
|
||||
Libertree = 'libertree',
|
||||
Ostatus = 'ostatus',
|
||||
Pumpio = 'pumpio',
|
||||
Tent = 'tent',
|
||||
Xmpp = 'xmpp',
|
||||
Zot = 'zot'
|
||||
}
|
||||
/** The third party sites this server can connect to via their application API. */
|
||||
export interface Services {
|
||||
/** The third party sites this server can retrieve messages from for combined display with regular traffic. */
|
||||
inbound: Array<Inbound>
|
||||
/** The third party sites this server can publish messages to on the behalf of a user. */
|
||||
outbound: Array<Outbound>
|
||||
}
|
||||
/** The third party sites this server can retrieve messages from for combined display with regular traffic. */
|
||||
export enum Inbound {
|
||||
Atom1 = 'atom1',
|
||||
Gnusocial = 'gnusocial',
|
||||
Imap = 'imap',
|
||||
Pnut = 'pnut',
|
||||
Pop3 = 'pop3',
|
||||
Pumpio = 'pumpio',
|
||||
Rss2 = 'rss2',
|
||||
Twitter = 'twitter'
|
||||
}
|
||||
/** The third party sites this server can publish messages to on the behalf of a user. */
|
||||
export enum Outbound {
|
||||
Atom1 = 'atom1',
|
||||
Blogger = 'blogger',
|
||||
Buddycloud = 'buddycloud',
|
||||
Diaspora = 'diaspora',
|
||||
Dreamwidth = 'dreamwidth',
|
||||
Drupal = 'drupal',
|
||||
Facebook = 'facebook',
|
||||
Friendica = 'friendica',
|
||||
Gnusocial = 'gnusocial',
|
||||
Google = 'google',
|
||||
Insanejournal = 'insanejournal',
|
||||
Libertree = 'libertree',
|
||||
Linkedin = 'linkedin',
|
||||
Livejournal = 'livejournal',
|
||||
Mediagoblin = 'mediagoblin',
|
||||
Myspace = 'myspace',
|
||||
Pinterest = 'pinterest',
|
||||
Pnut = 'pnut',
|
||||
Posterous = 'posterous',
|
||||
Pumpio = 'pumpio',
|
||||
Redmatrix = 'redmatrix',
|
||||
Rss2 = 'rss2',
|
||||
Smtp = 'smtp',
|
||||
Tent = 'tent',
|
||||
Tumblr = 'tumblr',
|
||||
Twitter = 'twitter',
|
||||
Wordpress = 'wordpress',
|
||||
Xmpp = 'xmpp'
|
||||
}
|
||||
/** Usage statistics for this server. */
|
||||
export interface Usage {
|
||||
users: Users
|
||||
localPosts: number | null
|
||||
localComments: number | null
|
||||
}
|
||||
/** statistics about the users of this server. */
|
||||
export interface Users {
|
||||
total: number | null
|
||||
activeHalfyear: number | null
|
||||
activeMonth: number | null
|
||||
}
|
||||
export function watchNote(watcherId: string, noteAuthorId: string, noteId: string): Promise<void>
|
||||
export function unwatchNote(watcherId: string, noteId: string): Promise<void>
|
||||
export enum PushNotificationKind {
|
||||
|
@ -1331,6 +1414,6 @@ export function getTimestamp(id: string): number
|
|||
export function genId(): string
|
||||
/** Generate an ID using a specific datetime */
|
||||
export function genIdAt(date: Date): string
|
||||
/** Generate random string based on [thread_rng] and [Alphanumeric]. */
|
||||
/** Generates a random string based on [thread_rng] and [Alphanumeric]. */
|
||||
export function generateSecureRandomString(length: number): string
|
||||
export function generateUserToken(): string
|
||||
|
|
|
@ -310,7 +310,7 @@ if (!nativeBinding) {
|
|||
throw new Error(`Failed to load native binding`)
|
||||
}
|
||||
|
||||
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, greet, initializeRustLogger, showServerInfo, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isQuote, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrc, DriveFileUsageHint, MutedNoteReason, NoteVisibility, NotificationType, PageVisibility, PollNoteVisibility, RelayStatus, UserEmojiModPerm, UserProfileFfvisibility, UserProfileMutingNotificationTypes, updateAntennasOnNewNote, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, Protocol, Inbound, Outbound, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
|
||||
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, fetchMeta, updateMetaCache, metaToPugArgs, loadConfig, stringToAcct, acctToString, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, Protocol, Inbound, Outbound, greet, initializeRustLogger, showServerInfo, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isQuote, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrc, DriveFileUsageHint, MutedNoteReason, NoteVisibility, NotificationType, PageVisibility, PollNoteVisibility, RelayStatus, UserEmojiModPerm, UserProfileFfvisibility, UserProfileMutingNotificationTypes, updateAntennasOnNewNote, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
|
||||
|
||||
module.exports.SECOND = SECOND
|
||||
module.exports.MINUTE = MINUTE
|
||||
|
@ -320,9 +320,18 @@ module.exports.USER_ONLINE_THRESHOLD = USER_ONLINE_THRESHOLD
|
|||
module.exports.USER_ACTIVE_THRESHOLD = USER_ACTIVE_THRESHOLD
|
||||
module.exports.FILE_TYPE_BROWSERSAFE = FILE_TYPE_BROWSERSAFE
|
||||
module.exports.loadEnv = loadEnv
|
||||
module.exports.fetchMeta = fetchMeta
|
||||
module.exports.updateMetaCache = updateMetaCache
|
||||
module.exports.metaToPugArgs = metaToPugArgs
|
||||
module.exports.loadConfig = loadConfig
|
||||
module.exports.stringToAcct = stringToAcct
|
||||
module.exports.acctToString = acctToString
|
||||
module.exports.fetchNodeinfo = fetchNodeinfo
|
||||
module.exports.nodeinfo_2_1 = nodeinfo_2_1
|
||||
module.exports.nodeinfo_2_0 = nodeinfo_2_0
|
||||
module.exports.Protocol = Protocol
|
||||
module.exports.Inbound = Inbound
|
||||
module.exports.Outbound = Outbound
|
||||
module.exports.greet = greet
|
||||
module.exports.initializeRustLogger = initializeRustLogger
|
||||
module.exports.showServerInfo = showServerInfo
|
||||
|
@ -346,8 +355,6 @@ module.exports.isSafeUrl = isSafeUrl
|
|||
module.exports.latestVersion = latestVersion
|
||||
module.exports.toMastodonId = toMastodonId
|
||||
module.exports.fromMastodonId = fromMastodonId
|
||||
module.exports.fetchMeta = fetchMeta
|
||||
module.exports.metaToPugArgs = metaToPugArgs
|
||||
module.exports.nyaify = nyaify
|
||||
module.exports.hashPassword = hashPassword
|
||||
module.exports.verifyPassword = verifyPassword
|
||||
|
@ -372,12 +379,6 @@ module.exports.UserEmojiModPerm = UserEmojiModPerm
|
|||
module.exports.UserProfileFfvisibility = UserProfileFfvisibility
|
||||
module.exports.UserProfileMutingNotificationTypes = UserProfileMutingNotificationTypes
|
||||
module.exports.updateAntennasOnNewNote = updateAntennasOnNewNote
|
||||
module.exports.fetchNodeinfo = fetchNodeinfo
|
||||
module.exports.nodeinfo_2_1 = nodeinfo_2_1
|
||||
module.exports.nodeinfo_2_0 = nodeinfo_2_0
|
||||
module.exports.Protocol = Protocol
|
||||
module.exports.Inbound = Inbound
|
||||
module.exports.Outbound = Outbound
|
||||
module.exports.watchNote = watchNote
|
||||
module.exports.unwatchNote = unwatchNote
|
||||
module.exports.PushNotificationKind = PushNotificationKind
|
||||
|
|
|
@ -1,24 +1,27 @@
|
|||
#[crate::export]
|
||||
//! This module is used in the TypeScript backend only.
|
||||
|
||||
#[crate::ts_export]
|
||||
pub const SECOND: i32 = 1000;
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const MINUTE: i32 = 60 * SECOND;
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const HOUR: i32 = 60 * MINUTE;
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const DAY: i32 = 24 * HOUR;
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const USER_ONLINE_THRESHOLD: i32 = 10 * MINUTE;
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const USER_ACTIVE_THRESHOLD: i32 = 3 * DAY;
|
||||
|
||||
/// List of file types allowed to be viewed directly in the browser
|
||||
///
|
||||
/// Anything not included here will be responded as application/octet-stream
|
||||
/// SVG is not allowed because it generates XSS <- we need to fix this and later allow it to be viewed directly
|
||||
/// SVG is not allowed because it generates XSS (TODO: fix this and later allow it to be viewed directly)
|
||||
/// * <https://github.com/sindresorhus/file-type/blob/main/supported.js>
|
||||
/// * <https://github.com/sindresorhus/file-type/blob/main/core.js>
|
||||
/// * <https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers>
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const FILE_TYPE_BROWSERSAFE: [&str; 41] = [
|
||||
// Images
|
||||
"image/png",
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Environment options
|
||||
|
||||
// FIXME: Are these options used?
|
||||
#[crate::export(object)]
|
||||
pub struct EnvConfig {
|
||||
|
|
|
@ -1,18 +1,29 @@
|
|||
//! Server information
|
||||
|
||||
use crate::database::db_conn;
|
||||
use crate::model::entity::meta;
|
||||
use rand::prelude::*;
|
||||
use sea_orm::{prelude::*, ActiveValue};
|
||||
use std::sync::Mutex;
|
||||
|
||||
type Meta = meta::Model;
|
||||
|
||||
static CACHE: Mutex<Option<Meta>> = Mutex::new(None);
|
||||
fn update_cache(meta: &Meta) {
|
||||
fn set_cache(meta: &Meta) {
|
||||
let _ = CACHE.lock().map(|mut cache| *cache = Some(meta.clone()));
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub async fn fetch_meta(use_cache: bool) -> Result<Meta, DbErr> {
|
||||
#[crate::export(js_name = "fetchMeta")]
|
||||
pub async fn local_server_info() -> Result<Meta, DbErr> {
|
||||
local_server_info_impl(true).await
|
||||
}
|
||||
|
||||
#[crate::export(js_name = "updateMetaCache")]
|
||||
pub async fn update() -> Result<(), DbErr> {
|
||||
local_server_info_impl(false).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn local_server_info_impl(use_cache: bool) -> Result<Meta, DbErr> {
|
||||
// try using cache
|
||||
if use_cache {
|
||||
if let Some(cache) = CACHE.lock().ok().and_then(|cache| cache.clone()) {
|
||||
|
@ -24,7 +35,7 @@ pub async fn fetch_meta(use_cache: bool) -> Result<Meta, DbErr> {
|
|||
let db = db_conn().await?;
|
||||
let meta = meta::Entity::find().one(db).await?;
|
||||
if let Some(meta) = meta {
|
||||
update_cache(&meta);
|
||||
set_cache(&meta);
|
||||
return Ok(meta);
|
||||
}
|
||||
|
||||
|
@ -35,7 +46,7 @@ pub async fn fetch_meta(use_cache: bool) -> Result<Meta, DbErr> {
|
|||
})
|
||||
.exec_with_returning(db)
|
||||
.await?;
|
||||
update_cache(&meta);
|
||||
set_cache(&meta);
|
||||
Ok(meta)
|
||||
}
|
||||
|
||||
|
@ -52,8 +63,9 @@ pub struct PugArgs {
|
|||
pub private_mode: Option<bool>,
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub fn meta_to_pug_args(meta: Meta) -> PugArgs {
|
||||
use rand::prelude::*;
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let splash_icon = meta
|
|
@ -1,5 +1,9 @@
|
|||
//! Server configurations and environment variables
|
||||
|
||||
pub use meta::local_server_info;
|
||||
pub use server::CONFIG;
|
||||
|
||||
pub mod constant;
|
||||
pub mod environment;
|
||||
pub mod meta;
|
||||
pub mod server;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Server configuration
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::Deserialize;
|
||||
use std::env;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Utilities for using Redis cache
|
||||
|
||||
use crate::database::{redis_conn, redis_key, RedisConnError};
|
||||
use redis::{AsyncCommands, RedisError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -21,10 +23,8 @@ pub enum Error {
|
|||
Redis(#[from] RedisError),
|
||||
#[error("Redis connection error: {0}")]
|
||||
RedisConn(#[from] RedisConnError),
|
||||
#[error("Data serialization error: {0}")]
|
||||
Serialize(#[from] rmp_serde::encode::Error),
|
||||
#[error("Data deserialization error: {0}")]
|
||||
Deserialize(#[from] rmp_serde::decode::Error),
|
||||
#[error("Failed to encode the data: {0}")]
|
||||
Encode(#[from] rmp_serde::encode::Error),
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -46,27 +46,30 @@ fn wildcard(category: Category) -> String {
|
|||
///
|
||||
/// This overwrites the exsisting cache with the same key.
|
||||
///
|
||||
/// ## Arguments
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `key` - key (will be prefixed automatically)
|
||||
/// * `value` - (de)serializable value
|
||||
/// * `expire_seconds` - TTL
|
||||
/// * `key` : key (prefixed automatically)
|
||||
/// * `value` : (de)serializable value
|
||||
/// * `expire_seconds` : TTL
|
||||
///
|
||||
/// ## Example
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use backend_rs::database::cache;
|
||||
/// # tokio_test::block_on(async {
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let key = "apple";
|
||||
/// let data = "I want to cache this string".to_string();
|
||||
///
|
||||
/// // caches the data for 10 seconds
|
||||
/// cache::set(key, &data, 10).await;
|
||||
/// cache::set(key, &data, 10).await?;
|
||||
///
|
||||
/// // get the cache
|
||||
/// let cached_data = cache::get::<String>(key).await.unwrap();
|
||||
/// let cached_data = cache::get::<String>(key).await?;
|
||||
///
|
||||
/// assert!(cached_data.is_some());
|
||||
/// assert_eq!(data, cached_data.unwrap());
|
||||
/// # })
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn set<V: for<'a> Deserialize<'a> + Serialize>(
|
||||
key: &str,
|
||||
|
@ -89,34 +92,36 @@ pub async fn set<V: for<'a> Deserialize<'a> + Serialize>(
|
|||
/// If the Redis connection is fine, this returns `Ok(data)` where `data`
|
||||
/// is the cached value. Returns `Ok(None)` if there is no value corresponding to `key`.
|
||||
///
|
||||
/// ## Arguments
|
||||
/// # Argument
|
||||
///
|
||||
/// * `key` - key (will be prefixed automatically)
|
||||
/// * `key` : key (will be prefixed automatically)
|
||||
///
|
||||
/// ## Example
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use backend_rs::database::cache;
|
||||
/// # tokio_test::block_on(async {
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let key = "banana";
|
||||
/// let data = "I want to cache this string".to_string();
|
||||
///
|
||||
/// // set cache
|
||||
/// cache::set(key, &data, 10).await.unwrap();
|
||||
/// cache::set(key, &data, 10).await?;
|
||||
///
|
||||
/// // get cache
|
||||
/// let cached_data = cache::get::<String>(key).await.unwrap();
|
||||
/// let cached_data = cache::get::<String>(key).await?;
|
||||
/// assert!(cached_data.is_some());
|
||||
/// assert_eq!(data, cached_data.unwrap());
|
||||
///
|
||||
/// // get nonexistent (or expired) cache
|
||||
/// let no_cache = cache::get::<String>("nonexistent").await.unwrap();
|
||||
/// let no_cache = cache::get::<String>("nonexistent").await?;
|
||||
/// assert!(no_cache.is_none());
|
||||
/// # })
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn get<V: for<'a> Deserialize<'a> + Serialize>(key: &str) -> Result<Option<V>, Error> {
|
||||
let serialized_value: Option<Vec<u8>> = redis_conn().await?.get(prefix_key(key)).await?;
|
||||
Ok(match serialized_value {
|
||||
Some(v) => Some(rmp_serde::from_slice::<V>(v.as_ref())?),
|
||||
Some(v) => rmp_serde::from_slice::<V>(v.as_ref()).ok(),
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
|
@ -126,29 +131,30 @@ pub async fn get<V: for<'a> Deserialize<'a> + Serialize>(key: &str) -> Result<Op
|
|||
/// If the Redis connection is fine, this returns `Ok(())`
|
||||
/// regardless of whether the cache exists.
|
||||
///
|
||||
/// ## Arguments
|
||||
/// # Argument
|
||||
///
|
||||
/// * `key` - key (will be prefixed automatically)
|
||||
/// * `key` : key (prefixed automatically)
|
||||
///
|
||||
/// ## Example
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use backend_rs::database::cache;
|
||||
/// # tokio_test::block_on(async {
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let key = "chocolate";
|
||||
/// let value = "I want to cache this string".to_string();
|
||||
///
|
||||
/// // set cache
|
||||
/// cache::set(key, &value, 10).await.unwrap();
|
||||
/// cache::set(key, &value, 10).await?;
|
||||
///
|
||||
/// // delete the cache
|
||||
/// cache::delete("foo").await.unwrap();
|
||||
/// cache::delete("nonexistent").await.unwrap(); // this is okay
|
||||
/// cache::delete("foo").await?;
|
||||
/// cache::delete("nonexistent").await?; // this is okay
|
||||
///
|
||||
/// // the cache is gone
|
||||
/// let cached_value = cache::get::<String>("foo").await.unwrap();
|
||||
/// let cached_value = cache::get::<String>("foo").await?;
|
||||
/// assert!(cached_value.is_none());
|
||||
/// # })
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn delete(key: &str) -> Result<(), Error> {
|
||||
Ok(redis_conn().await?.del(prefix_key(key)).await?)
|
||||
|
@ -159,12 +165,12 @@ pub async fn delete(key: &str) -> Result<(), Error> {
|
|||
/// The usage is the same as [set], except that you need to
|
||||
/// use [get_one] and [delete_one] to get/delete the cache.
|
||||
///
|
||||
/// ## Arguments
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `category` - one of [Category]
|
||||
/// * `key` - key (will be prefixed automatically)
|
||||
/// * `value` - (de)serializable value
|
||||
/// * `expire_seconds` - TTL
|
||||
/// * `category` : one of [Category]
|
||||
/// * `key` : key (prefixed automatically)
|
||||
/// * `value` : (de)serializable value
|
||||
/// * `expire_seconds` : TTL
|
||||
pub async fn set_one<V: for<'a> Deserialize<'a> + Serialize>(
|
||||
category: Category,
|
||||
key: &str,
|
||||
|
@ -178,10 +184,10 @@ pub async fn set_one<V: for<'a> Deserialize<'a> + Serialize>(
|
|||
///
|
||||
/// The usage is basically the same as [get].
|
||||
///
|
||||
/// ## Arguments
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `category` - one of [Category]
|
||||
/// * `key` - key (will be prefixed automatically)
|
||||
/// * `category` : one of [Category]
|
||||
/// * `key` : key (prefixed automatically)
|
||||
pub async fn get_one<V: for<'a> Deserialize<'a> + Serialize>(
|
||||
category: Category,
|
||||
key: &str,
|
||||
|
@ -193,19 +199,19 @@ pub async fn get_one<V: for<'a> Deserialize<'a> + Serialize>(
|
|||
///
|
||||
/// The usage is basically the same as [delete].
|
||||
///
|
||||
/// ## Arguments
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `category` - one of [Category]
|
||||
/// * `key` - key (will be prefixed automatically)
|
||||
/// - `category` : one of [Category]
|
||||
/// - `key` : key (prefixed automatically)
|
||||
pub async fn delete_one(category: Category, key: &str) -> Result<(), Error> {
|
||||
delete(&categorize(category, key)).await
|
||||
}
|
||||
|
||||
/// Deletes all Redis caches under a `category`.
|
||||
///
|
||||
/// ## Arguments
|
||||
/// # Argument
|
||||
///
|
||||
/// * `category` - one of [Category]
|
||||
/// * `category` : one of [Category]
|
||||
pub async fn delete_all(category: Category) -> Result<(), Error> {
|
||||
let mut redis = redis_conn().await?;
|
||||
let keys: Vec<Vec<u8>> = redis.keys(wildcard(category)).await?;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Interfaces for accessing PostgreSQL and Redis
|
||||
|
||||
pub use postgresql::db_conn;
|
||||
pub use redis::key as redis_key;
|
||||
pub use redis::redis_conn;
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
//! PostgreSQL interface
|
||||
|
||||
use crate::config::CONFIG;
|
||||
use once_cell::sync::OnceCell;
|
||||
use sea_orm::{ConnectOptions, Database, DbConn, DbErr};
|
||||
use std::time::Duration;
|
||||
use tracing::log::LevelFilter;
|
||||
|
||||
static DB_CONN: OnceCell<DbConn> = OnceCell::new();
|
||||
|
||||
async fn init_database() -> Result<&'static DbConn, DbErr> {
|
||||
async fn init_conn() -> Result<&'static DbConn, DbErr> {
|
||||
let database_uri = format!(
|
||||
"postgres://{}:{}@{}:{}/{}",
|
||||
CONFIG.db.user,
|
||||
|
@ -16,6 +19,7 @@ async fn init_database() -> Result<&'static DbConn, DbErr> {
|
|||
);
|
||||
let option: ConnectOptions = ConnectOptions::new(database_uri)
|
||||
.sqlx_logging_level(LevelFilter::Trace)
|
||||
.sqlx_slow_statements_logging_settings(LevelFilter::Warn, Duration::from_secs(3))
|
||||
.to_owned();
|
||||
|
||||
tracing::info!("initializing connection");
|
||||
|
@ -24,10 +28,11 @@ async fn init_database() -> Result<&'static DbConn, DbErr> {
|
|||
Ok(DB_CONN.get_or_init(move || conn))
|
||||
}
|
||||
|
||||
/// Returns an async PostgreSQL connection that can be used with [sea_orm] utilities.
|
||||
pub async fn db_conn() -> Result<&'static DbConn, DbErr> {
|
||||
match DB_CONN.get() {
|
||||
Some(conn) => Ok(conn),
|
||||
None => init_database().await,
|
||||
None => init_conn().await,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
//! Redis interface
|
||||
|
||||
use crate::config::CONFIG;
|
||||
use async_trait::async_trait;
|
||||
use bb8::{ManageConnection, Pool, PooledConnection, RunError};
|
||||
use redis::{aio::MultiplexedConnection, Client, ErrorKind, IntoConnectionInfo, RedisError};
|
||||
use tokio::sync::OnceCell;
|
||||
|
||||
/// A `bb8::ManageConnection` for `redis::Client::get_multiplexed_async_connection`.
|
||||
/// A [bb8::ManageConnection] for [redis::Client::get_multiplexed_async_connection].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RedisConnectionManager {
|
||||
client: Client,
|
||||
}
|
||||
|
||||
impl RedisConnectionManager {
|
||||
/// Create a new `RedisConnectionManager`.
|
||||
/// See `redis::Client::open` for a description of the parameter types.
|
||||
/// Creates a new [RedisConnectionManager].
|
||||
/// See [redis::Client::open] for a description of the parameter types.
|
||||
pub fn new<T: IntoConnectionInfo>(info: T) -> Result<Self, RedisError> {
|
||||
Ok(Self {
|
||||
client: Client::open(info.into_connection_info()?)?,
|
||||
|
@ -85,6 +87,7 @@ pub enum RedisConnError {
|
|||
Bb8Pool(RunError<RedisError>),
|
||||
}
|
||||
|
||||
/// Returns an async [redis] connection managed by a [bb8] connection pool.
|
||||
pub async fn redis_conn(
|
||||
) -> Result<PooledConnection<'static, RedisConnectionManager>, RedisConnError> {
|
||||
if !CONN_POOL.initialized() {
|
||||
|
@ -103,7 +106,7 @@ pub async fn redis_conn(
|
|||
.map_err(RedisConnError::Bb8Pool)
|
||||
}
|
||||
|
||||
/// prefix redis key
|
||||
/// prefix Redis key
|
||||
#[inline]
|
||||
pub fn key(key: impl ToString) -> String {
|
||||
format!("{}:{}", CONFIG.redis_key_prefix, key.to_string())
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
//! Services used to federate with other servers
|
||||
|
||||
pub mod acct;
|
||||
pub mod nodeinfo;
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
use crate::service::nodeinfo::schema::*;
|
||||
//! NodeInfo fetcher
|
||||
//!
|
||||
//! ref: <https://nodeinfo.diaspora.software/protocol.html>
|
||||
|
||||
use crate::federation::nodeinfo::schema::*;
|
||||
use crate::util::http_client;
|
||||
use isahc::AsyncReadResponseExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Deserialize;
|
||||
|
||||
/// Errors that can occur while fetching NodeInfo from a remote server
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("HTTP client aquisition error: {0}")]
|
||||
|
@ -19,25 +24,23 @@ pub enum Error {
|
|||
MissingNodeinfo,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
/// Represents the schema of `/.well-known/nodeinfo`.
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct NodeinfoLinks {
|
||||
links: Vec<NodeinfoLink>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
/// Represents one entry of `/.well-known/nodeinfo`.
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct NodeinfoLink {
|
||||
rel: String,
|
||||
href: String,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wellknown_nodeinfo_url(host: &str) -> String {
|
||||
format!("https://{}/.well-known/nodeinfo", host)
|
||||
}
|
||||
|
||||
/// Fetches `/.well-known/nodeinfo` and parses the result.
|
||||
async fn fetch_nodeinfo_links(host: &str) -> Result<NodeinfoLinks, Error> {
|
||||
let client = http_client::client()?;
|
||||
let wellknown_url = wellknown_nodeinfo_url(host);
|
||||
let wellknown_url = format!("https://{}/.well-known/nodeinfo", host);
|
||||
let mut wellknown_response = client.get_async(&wellknown_url).await?;
|
||||
|
||||
if !wellknown_response.status().is_success() {
|
||||
|
@ -52,6 +55,9 @@ async fn fetch_nodeinfo_links(host: &str) -> Result<NodeinfoLinks, Error> {
|
|||
Ok(serde_json::from_str(&wellknown_response.text().await?)?)
|
||||
}
|
||||
|
||||
/// Check if any of the following relations is present in the given [NodeinfoLinks].
|
||||
/// * <http://nodeinfo.diaspora.software/ns/schema/2.0>
|
||||
/// * <http://nodeinfo.diaspora.software/ns/schema/2.1>
|
||||
fn check_nodeinfo_link(links: NodeinfoLinks) -> Result<String, Error> {
|
||||
for link in links.links {
|
||||
if link.rel == "http://nodeinfo.diaspora.software/ns/schema/2.1"
|
||||
|
@ -64,6 +70,7 @@ fn check_nodeinfo_link(links: NodeinfoLinks) -> Result<String, Error> {
|
|||
Err(Error::MissingNodeinfo)
|
||||
}
|
||||
|
||||
/// Fetches the nodeinfo from the given URL and parses the result.
|
||||
async fn fetch_nodeinfo_impl(nodeinfo_link: &str) -> Result<Nodeinfo20, Error> {
|
||||
let client = http_client::client()?;
|
||||
let mut response = client.get_async(nodeinfo_link).await?;
|
||||
|
@ -83,6 +90,7 @@ async fn fetch_nodeinfo_impl(nodeinfo_link: &str) -> Result<Nodeinfo20, Error> {
|
|||
// for napi export
|
||||
type Nodeinfo = Nodeinfo20;
|
||||
|
||||
/// Fetches and returns the NodeInfo (version 2.0) of a remote server.
|
||||
#[crate::export]
|
||||
pub async fn fetch_nodeinfo(host: &str) -> Result<Nodeinfo, Error> {
|
||||
tracing::info!("fetching from {}", host);
|
|
@ -1,12 +1,14 @@
|
|||
use crate::config::CONFIG;
|
||||
//! NodeInfo generator
|
||||
|
||||
use crate::config::{local_server_info, CONFIG};
|
||||
use crate::database::{cache, db_conn};
|
||||
use crate::misc::meta::fetch_meta;
|
||||
use crate::federation::nodeinfo::schema::*;
|
||||
use crate::model::entity::{note, user};
|
||||
use crate::service::nodeinfo::schema::*;
|
||||
use sea_orm::{ColumnTrait, DbErr, EntityTrait, PaginatorTrait, QueryFilter};
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Errors that can occur while generating NodeInfo of the local server
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Database error: {0}")]
|
||||
|
@ -17,6 +19,14 @@ pub enum Error {
|
|||
Json(#[from] serde_json::Error),
|
||||
}
|
||||
|
||||
/// Fetches the number of total/active local users and local posts.
|
||||
///
|
||||
/// # Return value
|
||||
/// A tuple containing the following information in this order:
|
||||
/// * the total number of local users
|
||||
/// * the total number of local users active in the last 6 months
|
||||
/// * the total number of local users active in the last month (MAU)
|
||||
/// * the total number of posts from local users
|
||||
async fn statistics() -> Result<(u64, u64, u64, u64), DbErr> {
|
||||
let db = db_conn().await?;
|
||||
|
||||
|
@ -47,10 +57,12 @@ async fn statistics() -> Result<(u64, u64, u64, u64), DbErr> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Generates NodeInfo (version 2.1) of the local server.
|
||||
/// This function doesn't use caches and returns the latest information.
|
||||
async fn generate_nodeinfo_2_1() -> Result<Nodeinfo21, Error> {
|
||||
let (local_users, local_active_halfyear, local_active_month, local_posts) =
|
||||
statistics().await?;
|
||||
let meta = fetch_meta(true).await?;
|
||||
let meta = local_server_info().await?;
|
||||
let metadata = HashMap::from([
|
||||
(
|
||||
"nodeName".to_string(),
|
||||
|
@ -112,6 +124,7 @@ async fn generate_nodeinfo_2_1() -> Result<Nodeinfo21, Error> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns NodeInfo (version 2.1) of the local server.
|
||||
pub async fn nodeinfo_2_1() -> Result<Nodeinfo21, Error> {
|
||||
const NODEINFO_2_1_CACHE_KEY: &str = "nodeinfo_2_1";
|
||||
|
||||
|
@ -126,6 +139,7 @@ pub async fn nodeinfo_2_1() -> Result<Nodeinfo21, Error> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns NodeInfo (version 2.0) of the local server.
|
||||
pub async fn nodeinfo_2_0() -> Result<Nodeinfo20, Error> {
|
||||
Ok(nodeinfo_2_1().await?.into())
|
||||
}
|
7
packages/backend-rs/src/federation/nodeinfo/mod.rs
Normal file
7
packages/backend-rs/src/federation/nodeinfo/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
//! NodeInfo handler
|
||||
//!
|
||||
//! ref: <https://nodeinfo.diaspora.software/>
|
||||
|
||||
pub mod fetch;
|
||||
pub mod generate;
|
||||
mod schema;
|
|
@ -1,9 +1,13 @@
|
|||
//! Schema definitions of NodeInfo version 2.0 and 2.1
|
||||
//!
|
||||
//! ref: <https://nodeinfo.diaspora.software/schema.html>
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
// TODO: I want to use these macros but they don't work with rmp_serde
|
||||
// - #[serde(skip_serializing_if = "Option::is_none")] (https://github.com/3Hren/msgpack-rust/issues/86)
|
||||
// - #[serde(tag = "version", rename = "2.1")] (https://github.com/3Hren/msgpack-rust/issues/318)
|
||||
// * #[serde(skip_serializing_if = "Option::is_none")] (https://github.com/3Hren/msgpack-rust/issues/86)
|
||||
// * #[serde(tag = "version", rename = "2.1")] (https://github.com/3Hren/msgpack-rust/issues/318)
|
||||
|
||||
/// NodeInfo schema version 2.1. <https://nodeinfo.diaspora.software/docson/index.html#/ns/schema/2.1>
|
||||
#[derive(Deserialize, Serialize, Debug, PartialEq)]
|
|
@ -11,6 +11,7 @@ const GREETING_MESSAGE: &str = "\
|
|||
If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish
|
||||
";
|
||||
|
||||
/// Prints the greeting message and the Firefish version to stdout.
|
||||
#[crate::export]
|
||||
pub fn greet() {
|
||||
println!("{}", GREETING_MESSAGE);
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::config::CONFIG;
|
|||
use tracing::Level;
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
/// Initializes the [tracing] logger.
|
||||
#[crate::export(js_name = "initializeRustLogger")]
|
||||
pub fn initialize_logger() {
|
||||
let mut builder = FmtSubscriber::builder();
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Initializers
|
||||
|
||||
pub mod greet;
|
||||
pub mod log;
|
||||
pub mod system_info;
|
||||
|
|
|
@ -5,10 +5,21 @@ pub type SysinfoPoisonError = PoisonError<MutexGuard<'static, System>>;
|
|||
|
||||
static SYSTEM_INFO: OnceLock<Mutex<System>> = OnceLock::new();
|
||||
|
||||
pub fn system_info() -> &'static std::sync::Mutex<sysinfo::System> {
|
||||
/// 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.
|
||||
#[crate::export]
|
||||
pub fn show_server_info() -> Result<(), SysinfoPoisonError> {
|
||||
let system_info = system_info().lock()?;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pub use macro_rs::{export, ts_export};
|
||||
use macro_rs::{export, ts_export};
|
||||
|
||||
pub mod config;
|
||||
pub mod database;
|
||||
|
|
|
@ -1,13 +1,26 @@
|
|||
use crate::misc::meta::fetch_meta;
|
||||
use sea_orm::DbErr;
|
||||
//! This module is used in the TypeScript backend only.
|
||||
// We may want to (re)implement these functions in the `federation` module
|
||||
// in a Rusty way (e.g., traits of server type) if needed.
|
||||
|
||||
/// Checks if a server is blocked.
|
||||
///
|
||||
/// ## Argument
|
||||
/// # Argument
|
||||
/// `host` - punycoded instance host
|
||||
#[crate::export]
|
||||
pub async fn is_blocked_server(host: &str) -> Result<bool, DbErr> {
|
||||
Ok(fetch_meta(true)
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # use backend_rs::misc::check_server_block::is_blocked_server;
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// assert_eq!(true, is_blocked_server("blocked.com").await?);
|
||||
/// assert_eq!(false, is_blocked_server("not-blocked.com").await?);
|
||||
/// assert_eq!(true, is_blocked_server("subdomain.of.blocked.com").await?);
|
||||
/// assert_eq!(true, is_blocked_server("xn--l8jegik.blocked.com").await?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[crate::ts_export]
|
||||
pub async fn is_blocked_server(host: &str) -> Result<bool, sea_orm::DbErr> {
|
||||
Ok(crate::config::local_server_info()
|
||||
.await?
|
||||
.blocked_hosts
|
||||
.iter()
|
||||
|
@ -18,11 +31,23 @@ pub async fn is_blocked_server(host: &str) -> Result<bool, DbErr> {
|
|||
|
||||
/// Checks if a server is silenced.
|
||||
///
|
||||
/// ## Argument
|
||||
/// # Argument
|
||||
/// `host` - punycoded instance host
|
||||
#[crate::export]
|
||||
pub async fn is_silenced_server(host: &str) -> Result<bool, DbErr> {
|
||||
Ok(fetch_meta(true)
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # use backend_rs::misc::check_server_block::is_silenced_server;
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// assert_eq!(true, is_silenced_server("silenced.com").await?);
|
||||
/// assert_eq!(false, is_silenced_server("not-silenced.com").await?);
|
||||
/// assert_eq!(true, is_silenced_server("subdomain.of.silenced.com").await?);
|
||||
/// assert_eq!(true, is_silenced_server("xn--l8jegik.silenced.com").await?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[crate::ts_export]
|
||||
pub async fn is_silenced_server(host: &str) -> Result<bool, sea_orm::DbErr> {
|
||||
Ok(crate::config::local_server_info()
|
||||
.await?
|
||||
.silenced_hosts
|
||||
.iter()
|
||||
|
@ -34,11 +59,23 @@ pub async fn is_silenced_server(host: &str) -> Result<bool, DbErr> {
|
|||
/// Checks if a server is allowlisted.
|
||||
/// Returns `Ok(true)` if private mode is disabled.
|
||||
///
|
||||
/// ## Argument
|
||||
/// # Argument
|
||||
/// `host` - punycoded instance host
|
||||
#[crate::export]
|
||||
pub async fn is_allowed_server(host: &str) -> Result<bool, DbErr> {
|
||||
let meta = fetch_meta(true).await?;
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # use backend_rs::misc::check_server_block::is_allowed_server;
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// assert_eq!(true, is_allowed_server("allowed.com").await?);
|
||||
/// assert_eq!(false, is_allowed_server("not-allowed.com").await?);
|
||||
/// assert_eq!(false, is_allowed_server("subdomain.of.allowed.com").await?);
|
||||
/// assert_eq!(false, is_allowed_server("xn--l8jegik.allowed.com").await?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[crate::ts_export]
|
||||
pub async fn is_allowed_server(host: &str) -> Result<bool, sea_orm::DbErr> {
|
||||
let meta = crate::config::local_server_info().await?;
|
||||
|
||||
if !meta.private_mode.unwrap_or(false) {
|
||||
return Ok(true);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::misc::get_note_all_texts::{all_texts, NoteLike};
|
||||
use crate::misc::get_note_all_texts::{all_texts, PartialNoteToElaborate};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use sea_orm::DbErr;
|
||||
|
@ -26,9 +26,23 @@ fn check_word_mute_impl(
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns whether `note` should be hard-muted.
|
||||
///
|
||||
/// More specifically, this function returns `Ok(true)`
|
||||
/// if and only if one or more of these conditions are met:
|
||||
///
|
||||
/// * the note (text or CW) contains any of the words/patterns
|
||||
/// * the "parent" note(s) (reply, quote) contain any of the words/patterns
|
||||
/// * the alt text of the attached files contains any of the words/patterns
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `note` : [PartialNoteToElaborate] object
|
||||
/// * `muted_words` : list of muted keyword lists (each array item is a space-separated keyword list that represents an AND condition)
|
||||
/// * `muted_patterns` : list of JavaScript-style (e.g., `/foo/i`) regular expressions
|
||||
#[crate::export]
|
||||
pub async fn check_word_mute(
|
||||
note: NoteLike,
|
||||
note: PartialNoteToElaborate,
|
||||
muted_words: &[String],
|
||||
muted_patterns: &[String],
|
||||
) -> Result<bool, DbErr> {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::config::CONFIG;
|
||||
//! This module is used in the TypeScript backend only.
|
||||
// We may want to (re)implement these functions in the `federation` module
|
||||
// in a Rusty way (e.g., traits of actor type) if needed.
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
|
@ -10,28 +12,28 @@ pub enum Error {
|
|||
NoHostname,
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub fn get_full_ap_account(username: &str, host: Option<&str>) -> Result<String, Error> {
|
||||
Ok(match host {
|
||||
Some(host) => format!("{}@{}", username, to_puny(host)?),
|
||||
None => format!("{}@{}", username, extract_host(&CONFIG.url)?),
|
||||
None => format!("{}@{}", username, extract_host(&crate::config::CONFIG.url)?),
|
||||
})
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub fn is_self_host(host: Option<&str>) -> Result<bool, Error> {
|
||||
Ok(match host {
|
||||
Some(host) => extract_host(&CONFIG.url)? == to_puny(host)?,
|
||||
Some(host) => extract_host(&crate::config::CONFIG.url)? == to_puny(host)?,
|
||||
None => true,
|
||||
})
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub fn is_same_origin(uri: &str) -> Result<bool, Error> {
|
||||
Ok(url::Url::parse(uri)?.origin().ascii_serialization() == CONFIG.url)
|
||||
Ok(url::Url::parse(uri)?.origin().ascii_serialization() == crate::config::CONFIG.url)
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub fn extract_host(uri: &str) -> Result<String, Error> {
|
||||
url::Url::parse(uri)?
|
||||
.host_str()
|
||||
|
@ -39,29 +41,7 @@ pub fn extract_host(uri: &str) -> Result<String, Error> {
|
|||
.and_then(|v| Ok(to_puny(v)?))
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub fn to_puny(host: &str) -> Result<String, idna::Errors> {
|
||||
idna::domain_to_ascii(host)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::{extract_host, to_puny};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn extract_host_test() {
|
||||
assert_eq!(
|
||||
extract_host("https://firefish.dev/firefish/firefish.git").unwrap(),
|
||||
"firefish.dev"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_puny_test() {
|
||||
assert_eq!(
|
||||
to_puny("何もかも.owari.shop").unwrap(),
|
||||
"xn--u8jyfb5762a.owari.shop"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! This module is used in the TypeScript backend only.
|
||||
|
||||
#[crate::ts_export]
|
||||
pub fn is_unicode_emoji(s: &str) -> bool {
|
||||
emojis::get(s).is_some()
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
/// Escapes `%` and `\` in the given string.
|
||||
#[crate::export]
|
||||
pub fn sql_like_escape(src: &str) -> String {
|
||||
src.replace('%', r"\%").replace('_', r"\_")
|
||||
}
|
||||
|
||||
/// Returns `true` if `src` does not contain suspicious characters like `%`.
|
||||
#[crate::export]
|
||||
pub fn safe_for_sql(src: &str) -> bool {
|
||||
!src.contains([
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/// Convert milliseconds to a human readable string
|
||||
/// Converts milliseconds to a human readable string.
|
||||
#[crate::export]
|
||||
pub fn format_milliseconds(milliseconds: u32) -> String {
|
||||
let mut seconds = milliseconds / 1000;
|
||||
|
|
|
@ -2,9 +2,8 @@ use crate::database::db_conn;
|
|||
use crate::model::entity::{drive_file, note};
|
||||
use sea_orm::{prelude::*, QuerySelect};
|
||||
|
||||
// TODO?: handle name collisions
|
||||
#[crate::export(object, js_name = "NoteLikeForAllTexts")]
|
||||
pub struct NoteLike {
|
||||
#[crate::export(object)]
|
||||
pub struct PartialNoteToElaborate {
|
||||
pub file_ids: Vec<String>,
|
||||
pub user_id: String,
|
||||
pub text: Option<String>,
|
||||
|
@ -16,11 +15,14 @@ pub struct NoteLike {
|
|||
/// Returns [`Vec<String>`] containing the post text, content warning,
|
||||
/// those of the "parent" (replied/quoted) posts, and alt texts of attached files.
|
||||
///
|
||||
/// ## Arguments
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `note` - [NoteLike] object
|
||||
/// * `include_parent` - whether to take the reply-to post and quoted post into account
|
||||
pub async fn all_texts(note: NoteLike, include_parent: bool) -> Result<Vec<String>, DbErr> {
|
||||
/// * `note` : [PartialNoteToElaborate] object
|
||||
/// * `include_parent` : whether to take the reply-to post and quoted post into account
|
||||
pub async fn all_texts(
|
||||
note: PartialNoteToElaborate,
|
||||
include_parent: bool,
|
||||
) -> Result<Vec<String>, DbErr> {
|
||||
let db = db_conn().await?;
|
||||
|
||||
let mut texts: Vec<String> = vec![];
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use serde::Deserialize;
|
||||
|
||||
// TODO?: handle name collisions
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, js_name = "NoteLikeForGetNoteSummary")]
|
||||
pub struct NoteLike {
|
||||
#[crate::export(object)]
|
||||
pub struct PartialNoteToSummarize {
|
||||
pub file_ids: Vec<String>,
|
||||
pub text: Option<String>,
|
||||
pub cw: Option<String>,
|
||||
|
@ -12,7 +11,7 @@ pub struct NoteLike {
|
|||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn get_note_summary(note: NoteLike) -> String {
|
||||
pub fn get_note_summary(note: PartialNoteToSummarize) -> String {
|
||||
let mut buf: Vec<String> = vec![];
|
||||
|
||||
if let Some(cw) = note.cw {
|
||||
|
@ -36,12 +35,12 @@ pub fn get_note_summary(note: NoteLike) -> String {
|
|||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::{get_note_summary, NoteLike};
|
||||
use super::{get_note_summary, PartialNoteToSummarize};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_note_summary() {
|
||||
let note = NoteLike {
|
||||
let note = PartialNoteToSummarize {
|
||||
file_ids: vec![],
|
||||
text: Some("Hello world!".to_string()),
|
||||
cw: None,
|
||||
|
@ -49,7 +48,7 @@ mod unit_test {
|
|||
};
|
||||
assert_eq!(get_note_summary(note), "Hello world!");
|
||||
|
||||
let note_with_cw = NoteLike {
|
||||
let note_with_cw = PartialNoteToSummarize {
|
||||
file_ids: vec![],
|
||||
text: Some("Hello world!".to_string()),
|
||||
cw: Some("Content warning".to_string()),
|
||||
|
@ -57,7 +56,7 @@ mod unit_test {
|
|||
};
|
||||
assert_eq!(get_note_summary(note_with_cw), "Content warning");
|
||||
|
||||
let note_with_file_and_cw = NoteLike {
|
||||
let note_with_file_and_cw = PartialNoteToSummarize {
|
||||
file_ids: vec!["9s7fmcqogiq4igin".to_string()],
|
||||
text: None,
|
||||
cw: Some("Selfie, no ec".to_string()),
|
||||
|
@ -65,7 +64,7 @@ mod unit_test {
|
|||
};
|
||||
assert_eq!(get_note_summary(note_with_file_and_cw), "Selfie, no ec 📎");
|
||||
|
||||
let note_with_files_only = NoteLike {
|
||||
let note_with_files_only = PartialNoteToSummarize {
|
||||
file_ids: vec![
|
||||
"9s7fmcqogiq4igin".to_string(),
|
||||
"9s7qrld5u14cey98".to_string(),
|
||||
|
@ -78,7 +77,7 @@ mod unit_test {
|
|||
};
|
||||
assert_eq!(get_note_summary(note_with_files_only), "📎 (4)");
|
||||
|
||||
let note_all = NoteLike {
|
||||
let note_all = PartialNoteToSummarize {
|
||||
file_ids: vec![
|
||||
"9s7fmcqogiq4igin".to_string(),
|
||||
"9s7qrld5u14cey98".to_string(),
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
//! Fetch latest Firefish version from the Firefish repository
|
||||
|
||||
use crate::database::cache;
|
||||
use crate::util::http_client;
|
||||
use isahc::ReadResponseExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
|
@ -23,7 +25,7 @@ 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, Serialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Response {
|
||||
version: String,
|
||||
}
|
||||
|
@ -43,6 +45,7 @@ async fn get_latest_version() -> Result<String, Error> {
|
|||
Ok(res_parsed.version)
|
||||
}
|
||||
|
||||
/// Returns the latest Firefish version.
|
||||
#[crate::export]
|
||||
pub async fn latest_version() -> Result<String, Error> {
|
||||
let version: Option<String> =
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Miscellaneous utilities
|
||||
|
||||
pub mod check_server_block;
|
||||
pub mod check_word_mute;
|
||||
pub mod convert_host;
|
||||
|
@ -11,7 +13,6 @@ pub mod is_quote;
|
|||
pub mod is_safe_url;
|
||||
pub mod latest_version;
|
||||
pub mod mastodon_id;
|
||||
pub mod meta;
|
||||
pub mod nyaify;
|
||||
pub mod password;
|
||||
pub mod reaction;
|
||||
|
|
|
@ -1,6 +1,25 @@
|
|||
//! Cat language converter
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::{Captures, Regex};
|
||||
|
||||
/// Converts the given text into the cat language.
|
||||
///
|
||||
/// refs:
|
||||
/// * <https://misskey-hub.net/ns/#isCat>
|
||||
/// * <https://firefish.dev/ns#speakAsCat>
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `text` : original text
|
||||
/// * `lang` : language code (e.g., `Some("en")`, `Some("en-US")`, `Some("uk-UA")`, `None`)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use backend_rs::misc::nyaify::nyaify;
|
||||
/// assert_eq!(nyaify("I'll take a nap.", Some("en")), "I'll take a nyap.");
|
||||
/// ```
|
||||
#[crate::export]
|
||||
pub fn nyaify(text: &str, lang: Option<&str>) -> String {
|
||||
let mut to_return = text.to_owned();
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
//! Utilities for password hash generation and verification
|
||||
|
||||
use argon2::{
|
||||
password_hash,
|
||||
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
|
||||
Argon2,
|
||||
};
|
||||
|
||||
/// Hashes the given password using [Argon2] algorithm.
|
||||
#[crate::export]
|
||||
pub fn hash_password(password: &str) -> Result<String, password_hash::errors::Error> {
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
|
@ -13,7 +16,7 @@ pub fn hash_password(password: &str) -> Result<String, password_hash::errors::Er
|
|||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum VerifyError {
|
||||
pub enum Error {
|
||||
#[error("An error occured while bcrypt verification: {0}")]
|
||||
Bcrypt(#[from] bcrypt::BcryptError),
|
||||
#[error("Invalid argon2 password hash: {0}")]
|
||||
|
@ -22,8 +25,9 @@ pub enum VerifyError {
|
|||
Argon2(#[from] argon2::Error),
|
||||
}
|
||||
|
||||
/// Checks whether the given password and hash match.
|
||||
#[crate::export]
|
||||
pub fn verify_password(password: &str, hash: &str) -> Result<bool, VerifyError> {
|
||||
pub fn verify_password(password: &str, hash: &str) -> Result<bool, Error> {
|
||||
if is_old_password_algorithm(hash) {
|
||||
Ok(bcrypt::verify(password, hash)?)
|
||||
} else {
|
||||
|
@ -34,6 +38,7 @@ pub fn verify_password(password: &str, hash: &str) -> Result<bool, VerifyError>
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns whether the [bcrypt] algorithm is used for the password hash.
|
||||
#[inline]
|
||||
#[crate::export]
|
||||
pub fn is_old_password_algorithm(hash: &str) -> bool {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::config::local_server_info;
|
||||
use crate::database::db_conn;
|
||||
use crate::misc::convert_host::to_puny;
|
||||
use crate::misc::meta::fetch_meta;
|
||||
use crate::model::entity::emoji;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
@ -87,7 +86,7 @@ pub async fn to_db_reaction(reaction: Option<&str>, host: Option<&str>) -> Resul
|
|||
|
||||
if let Some(host) = host {
|
||||
// remote emoji
|
||||
let ascii_host = to_puny(host)?;
|
||||
let ascii_host = idna::domain_to_ascii(host)?;
|
||||
|
||||
// TODO: Does SeaORM have the `exists` method?
|
||||
if emoji::Entity::find()
|
||||
|
@ -119,7 +118,7 @@ pub async fn to_db_reaction(reaction: Option<&str>, host: Option<&str>) -> Resul
|
|||
};
|
||||
};
|
||||
|
||||
Ok(fetch_meta(true).await?.default_reaction)
|
||||
Ok(local_server_info().await?.default_reaction)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
use crate::database::db_conn;
|
||||
use crate::model::entity::attestation_challenge;
|
||||
use chrono::{Duration, Utc};
|
||||
use sea_orm::{ColumnTrait, DbErr, EntityTrait, QueryFilter};
|
||||
use sea_orm::prelude::*;
|
||||
|
||||
/// Delete all entries in the "attestation_challenge" table created at more than 5 minutes ago
|
||||
/// Delete all entries in the [attestation_challenge] table created at more than 5 minutes ago
|
||||
#[crate::export]
|
||||
pub async fn remove_old_attestation_challenges() -> Result<(), DbErr> {
|
||||
let res = attestation_challenge::Entity::delete_many()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Utilities to check hardware information such as cpu, memory, storage usage
|
||||
|
||||
use crate::init::system_info::{system_info, SysinfoPoisonError};
|
||||
use sysinfo::{Disks, MemoryRefreshKind};
|
||||
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
//! Database structure, auto-generated by [sea_orm]
|
||||
|
||||
pub mod entity;
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::config::CONFIG;
|
|||
use crate::database::{cache, db_conn};
|
||||
use crate::federation::acct::Acct;
|
||||
use crate::model::entity::{antenna, blocking, following, note, sea_orm_active_enums::*};
|
||||
use sea_orm::{ColumnTrait, DbErr, EntityTrait, QueryFilter, QuerySelect};
|
||||
use sea_orm::{prelude::*, QuerySelect};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum AntennaCheckError {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::database::{cache, db_conn, redis_conn, redis_key, RedisConnError};
|
||||
use crate::federation::acct::Acct;
|
||||
use crate::misc::get_note_all_texts::{all_texts, NoteLike};
|
||||
use crate::misc::get_note_all_texts::{all_texts, PartialNoteToElaborate};
|
||||
use crate::model::entity::{antenna, note};
|
||||
use crate::service::antenna::check_hit::{check_hit_antenna, AntennaCheckError};
|
||||
use crate::service::stream;
|
||||
use crate::util::id::{get_timestamp, InvalidIdError};
|
||||
use redis::{streams::StreamMaxlen, AsyncCommands, RedisError};
|
||||
use sea_orm::{DbErr, EntityTrait};
|
||||
use sea_orm::prelude::*;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
|
@ -53,7 +53,7 @@ pub async fn update_antennas_on_new_note(
|
|||
) -> Result<(), Error> {
|
||||
let note_cloned = note.clone();
|
||||
let note_all_texts = all_texts(
|
||||
NoteLike {
|
||||
PartialNoteToElaborate {
|
||||
file_ids: note.file_ids,
|
||||
user_id: note.user_id,
|
||||
text: note.text,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Services provided for local users
|
||||
|
||||
pub mod antenna;
|
||||
pub mod nodeinfo;
|
||||
pub mod note;
|
||||
pub mod push_notification;
|
||||
pub mod stream;
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
pub mod fetch;
|
||||
pub mod generate;
|
||||
mod schema;
|
|
@ -1,7 +1,7 @@
|
|||
use crate::database::db_conn;
|
||||
use crate::model::entity::note_watching;
|
||||
use crate::util::id::gen_id;
|
||||
use sea_orm::{ActiveValue, ColumnTrait, DbErr, EntityTrait, ModelTrait, QueryFilter};
|
||||
use crate::util::id::gen_id_at;
|
||||
use sea_orm::{prelude::*, ActiveValue};
|
||||
|
||||
#[crate::export]
|
||||
pub async fn watch_note(
|
||||
|
@ -10,9 +10,11 @@ pub async fn watch_note(
|
|||
note_id: &str,
|
||||
) -> Result<(), DbErr> {
|
||||
if watcher_id != note_author_id {
|
||||
let now = chrono::Utc::now();
|
||||
|
||||
note_watching::Entity::insert(note_watching::ActiveModel {
|
||||
id: ActiveValue::set(gen_id()),
|
||||
created_at: ActiveValue::set(chrono::Utc::now().into()),
|
||||
id: ActiveValue::set(gen_id_at(now)),
|
||||
created_at: ActiveValue::set(now.into()),
|
||||
user_id: ActiveValue::Set(watcher_id.to_string()),
|
||||
note_user_id: ActiveValue::Set(note_author_id.to_string()),
|
||||
note_id: ActiveValue::Set(note_id.to_string()),
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::config::local_server_info;
|
||||
use crate::database::db_conn;
|
||||
use crate::misc::get_note_summary::{get_note_summary, NoteLike};
|
||||
use crate::misc::meta::fetch_meta;
|
||||
use crate::misc::get_note_summary::{get_note_summary, PartialNoteToSummarize};
|
||||
use crate::model::entity::sw_subscription;
|
||||
use crate::util::http_client;
|
||||
use once_cell::sync::OnceCell;
|
||||
use sea_orm::{prelude::*, DbErr};
|
||||
use sea_orm::prelude::*;
|
||||
use web_push::{
|
||||
ContentEncoding, IsahcWebPushClient, SubscriptionInfo, SubscriptionKeys, VapidSignatureBuilder,
|
||||
WebPushClient, WebPushError, WebPushMessageBuilder,
|
||||
|
@ -87,7 +87,7 @@ fn compact_content(
|
|||
));
|
||||
}
|
||||
|
||||
let note_like: NoteLike = serde_json::from_value(note.clone())?;
|
||||
let note_like: PartialNoteToSummarize = serde_json::from_value(note.clone())?;
|
||||
let text = get_note_summary(note_like);
|
||||
|
||||
let note_object = note.as_object_mut().unwrap();
|
||||
|
@ -102,7 +102,7 @@ fn compact_content(
|
|||
}
|
||||
|
||||
async fn handle_web_push_failure(
|
||||
db: &DatabaseConnection,
|
||||
db: &DbConn,
|
||||
err: WebPushError,
|
||||
subscription_id: &str,
|
||||
error_message: &str,
|
||||
|
@ -140,7 +140,7 @@ pub async fn send_push_notification(
|
|||
kind: PushNotificationKind,
|
||||
content: &serde_json::Value,
|
||||
) -> Result<(), Error> {
|
||||
let meta = fetch_meta(true).await?;
|
||||
let meta = local_server_info().await?;
|
||||
|
||||
if !meta.enable_service_worker || meta.sw_public_key.is_none() || meta.sw_private_key.is_none()
|
||||
{
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::service::stream::{publish_to_stream, Error, Stream};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Serialize;
|
||||
|
||||
// TODO: define schema type in other place
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object)]
|
||||
pub struct PackedEmoji {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::service::stream::{publish_to_stream, Error, Stream};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object)]
|
||||
pub struct AbuseUserReportLike {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Shared [isahc] HTTP client
|
||||
|
||||
use crate::config::CONFIG;
|
||||
use isahc::{config::*, HttpClient};
|
||||
use once_cell::sync::OnceCell;
|
||||
|
@ -13,6 +15,22 @@ pub enum Error {
|
|||
|
||||
static CLIENT: OnceCell<HttpClient> = OnceCell::new();
|
||||
|
||||
/// Returns an [HttpClient] that takes the proxy configuration into account.
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # use backend_rs::util::http_client::client;
|
||||
/// use isahc::ReadResponseExt;
|
||||
///
|
||||
/// # fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let mut response = client()?.get("https://example.com/")?;
|
||||
///
|
||||
/// if response.status().is_success() {
|
||||
/// println!("{}", response.text()?);
|
||||
/// }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn client() -> Result<HttpClient, Error> {
|
||||
CLIENT
|
||||
.get_or_try_init(|| {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Basic utilities such as ID generator and HTTP client
|
||||
|
||||
pub mod http_client;
|
||||
pub mod id;
|
||||
pub mod random;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//! Secure random string generator
|
||||
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
|
||||
/// Generate random string based on [thread_rng] and [Alphanumeric].
|
||||
/// Generates a random string based on [thread_rng] and [Alphanumeric].
|
||||
#[crate::export]
|
||||
pub fn generate_secure_random_string(length: u16) -> String {
|
||||
thread_rng()
|
||||
|
|
|
@ -1,23 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>Firefish API</title>
|
||||
<!-- needed for adaptive design -->
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!--
|
||||
ReDoc doesn't change outer page styles
|
||||
-->
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
<!-- Embed elements Elements via Web Component -->
|
||||
<script src="https://unpkg.com/@stoplight/elements/web-components.min.js" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@stoplight/elements/styles.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<redoc spec-url="/api.json" expand-responses="200" expand-single-schema-field="true"></redoc>
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0-rc.50/bundles/redoc.standalone.js" integrity="sha256-WJbngBWN9vp6vkEuzeoSj5tE5saW9Hfj6/SinkzhL2s=" crossorigin="anonymous"></script>
|
||||
<elements-api
|
||||
id="element-main"
|
||||
apiDescriptionUrl='/api.json'
|
||||
router="hash"
|
||||
layout="responsive"
|
||||
/>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -22,26 +22,26 @@
|
|||
"@swc/core-android-arm64": "1.3.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bull-board/api": "5.18.3",
|
||||
"@bull-board/koa": "5.18.3",
|
||||
"@bull-board/ui": "5.18.3",
|
||||
"@bull-board/api": "5.20.0",
|
||||
"@bull-board/koa": "5.20.0",
|
||||
"@bull-board/ui": "5.20.0",
|
||||
"@discordapp/twemoji": "15.0.3",
|
||||
"@koa/cors": "5.0.0",
|
||||
"@koa/multer": "3.0.2",
|
||||
"@koa/router": "12.0.1",
|
||||
"@ladjs/koa-views": "9.0.0",
|
||||
"@peertube/http-signature": "1.7.0",
|
||||
"@redocly/openapi-core": "1.13.0",
|
||||
"@redocly/openapi-core": "1.14.0",
|
||||
"@sinonjs/fake-timers": "11.2.2",
|
||||
"adm-zip": "0.5.10",
|
||||
"ajv": "8.14.0",
|
||||
"adm-zip": "0.5.14",
|
||||
"ajv": "8.16.0",
|
||||
"archiver": "7.0.1",
|
||||
"aws-sdk": "2.1628.0",
|
||||
"aws-sdk": "2.1635.0",
|
||||
"axios": "1.7.2",
|
||||
"backend-rs": "workspace:*",
|
||||
"blurhash": "2.0.5",
|
||||
"bull": "4.12.9",
|
||||
"cacheable-lookup": "TheEssem/cacheable-lookup",
|
||||
"cacheable-lookup": "git+https://github.com/TheEssem/cacheable-lookup.git#dd2fb616366a3c68dcf321a57a67295967b204bf",
|
||||
"cbor-x": "1.5.9",
|
||||
"chalk": "5.3.0",
|
||||
"cli-highlight": "2.1.11",
|
||||
|
@ -57,7 +57,7 @@
|
|||
"firefish-js": "workspace:*",
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"form-data": "4.0.0",
|
||||
"got": "14.3.0",
|
||||
"got": "14.4.0",
|
||||
"gunzip-maybe": "1.4.2",
|
||||
"hpagent": "1.2.0",
|
||||
"ioredis": "5.4.1",
|
||||
|
@ -87,9 +87,9 @@
|
|||
"node-fetch": "3.3.2",
|
||||
"nodemailer": "6.9.13",
|
||||
"opencc-js": "1.0.5",
|
||||
"otpauth": "9.2.4",
|
||||
"otpauth": "9.3.1",
|
||||
"parse5": "7.1.2",
|
||||
"pg": "8.11.5",
|
||||
"pg": "8.12.0",
|
||||
"private-ip": "3.0.2",
|
||||
"probe-image-size": "7.2.3",
|
||||
"promise-limit": "2.7.0",
|
||||
|
@ -122,14 +122,14 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@swc/cli": "0.3.12",
|
||||
"@swc/core": "1.5.7",
|
||||
"@swc/core": "1.5.25",
|
||||
"@types/adm-zip": "0.5.5",
|
||||
"@types/color-convert": "2.0.3",
|
||||
"@types/content-disposition": "0.5.8",
|
||||
"@types/escape-regexp": "0.0.3",
|
||||
"@types/fluent-ffmpeg": "2.1.24",
|
||||
"@types/jsdom": "21.1.6",
|
||||
"@types/jsonld": "1.5.13",
|
||||
"@types/jsdom": "21.1.7",
|
||||
"@types/jsonld": "1.5.14",
|
||||
"@types/jsrsasign": "10.5.14",
|
||||
"@types/katex": "0.16.7",
|
||||
"@types/koa": "2.15.0",
|
||||
|
@ -143,10 +143,10 @@
|
|||
"@types/koa__multer": "2.0.7",
|
||||
"@types/koa__router": "12.0.4",
|
||||
"@types/mocha": "10.0.6",
|
||||
"@types/node": "20.12.12",
|
||||
"@types/node": "20.14.2",
|
||||
"@types/node-fetch": "2.6.11",
|
||||
"@types/nodemailer": "6.4.15",
|
||||
"@types/oauth": "0.9.4",
|
||||
"@types/oauth": "0.9.5",
|
||||
"@types/opencc-js": "1.0.3",
|
||||
"@types/pg": "8.11.6",
|
||||
"@types/probe-image-size": "7.2.4",
|
||||
|
@ -168,7 +168,7 @@
|
|||
"@types/websocket": "1.0.10",
|
||||
"@types/ws": "8.5.10",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "9.3.0",
|
||||
"eslint": "9.4.0",
|
||||
"mocha": "10.4.0",
|
||||
"pug": "3.0.3",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
|
@ -176,7 +176,7 @@
|
|||
"ts-loader": "9.5.1",
|
||||
"ts-node": "10.9.2",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"type-fest": "4.18.3",
|
||||
"type-fest": "4.19.0",
|
||||
"typescript": "5.4.5",
|
||||
"webpack": "5.91.0",
|
||||
"ws": "8.17.0"
|
||||
|
|
|
@ -12,6 +12,8 @@ import { masterMain } from "./master.js";
|
|||
import { workerMain } from "./worker.js";
|
||||
import os from "node:os";
|
||||
|
||||
import { initializeRustLogger } from "backend-rs";
|
||||
|
||||
const logger = new Logger("core", "cyan");
|
||||
const clusterLogger = logger.createSubLogger("cluster", "orange", false);
|
||||
const ev = new Xev();
|
||||
|
@ -20,6 +22,8 @@ const ev = new Xev();
|
|||
* Init process
|
||||
*/
|
||||
export default async function () {
|
||||
initializeRustLogger();
|
||||
|
||||
const mode =
|
||||
process.env.mode && ["web", "queue"].includes(process.env.mode)
|
||||
? `(${process.env.mode})`
|
||||
|
|
|
@ -4,11 +4,10 @@ import semver from "semver";
|
|||
|
||||
import Logger from "@/services/logger.js";
|
||||
import {
|
||||
fetchMeta,
|
||||
greet,
|
||||
initializeRustLogger,
|
||||
removeOldAttestationChallenges,
|
||||
showServerInfo,
|
||||
updateMetaCache,
|
||||
type Config,
|
||||
} from "backend-rs";
|
||||
import { config, envOption } from "@/config.js";
|
||||
|
@ -24,7 +23,6 @@ const bootLogger = logger.createSubLogger("boot", "magenta", false);
|
|||
export async function masterMain() {
|
||||
// initialize app
|
||||
try {
|
||||
initializeRustLogger();
|
||||
greet();
|
||||
showEnvironment();
|
||||
showServerInfo();
|
||||
|
@ -55,7 +53,7 @@ export async function masterMain() {
|
|||
import("../daemons/server-stats.js").then((x) => x.default());
|
||||
import("../daemons/queue-stats.js").then((x) => x.default());
|
||||
// Update meta cache every 5 minitues
|
||||
setInterval(() => fetchMeta(false), 1000 * 60 * 5);
|
||||
setInterval(() => updateMetaCache(), 1000 * 60 * 5);
|
||||
// Remove old attestation challenges
|
||||
setInterval(() => removeOldAttestationChallenges(), 1000 * 60 * 30);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ export default async function () {
|
|||
ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length || 50));
|
||||
});
|
||||
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (!meta.enableServerMachineStats) return;
|
||||
|
||||
async function tick() {
|
||||
|
|
|
@ -217,7 +217,7 @@ export const db = new DataSource({
|
|||
: false,
|
||||
logging: log,
|
||||
logger: log ? new DbLogger() : undefined,
|
||||
maxQueryExecutionTime: 300,
|
||||
maxQueryExecutionTime: 3000,
|
||||
entities: entities,
|
||||
migrations: ["../../migration/*.js"],
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ import type { ILocalUser } from "@/models/entities/user.js";
|
|||
import { Users } from "@/models/index.js";
|
||||
|
||||
export async function fetchProxyAccount(): Promise<ILocalUser | null> {
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.proxyAccountId == null) return null;
|
||||
return (await Users.findOneByOrFail({
|
||||
id: meta.proxyAccountId,
|
||||
|
|
|
@ -26,7 +26,7 @@ export async function translate(
|
|||
from: PostLanguage | null,
|
||||
to: PostLanguage,
|
||||
) {
|
||||
const instance = await fetchMeta(true);
|
||||
const instance = await fetchMeta();
|
||||
|
||||
if (instance.deeplAuthKey == null && instance.libreTranslateApiUrl == null) {
|
||||
throw Error("No translator is set up on this server.");
|
||||
|
|
|
@ -6,7 +6,7 @@ import { downloadTextFile } from "@/misc/download-text-file.js";
|
|||
import { Users, DriveFiles, Mutings } from "@/models/index.js";
|
||||
import type { DbUserImportJobData } from "@/queue/types.js";
|
||||
import type { User } from "@/models/entities/user.js";
|
||||
import { genId, isSelfHost, stringToAcct, toPuny } from "backend-rs";
|
||||
import { genIdAt, isSelfHost, stringToAcct, toPuny } from "backend-rs";
|
||||
import { IsNull } from "typeorm";
|
||||
import { inspect } from "node:util";
|
||||
|
||||
|
@ -80,9 +80,11 @@ export async function importMuting(
|
|||
}
|
||||
|
||||
async function mute(user: User, target: User) {
|
||||
const now = new Date();
|
||||
|
||||
await Mutings.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
id: genIdAt(now),
|
||||
createdAt: now,
|
||||
muterId: user.id,
|
||||
muteeId: target.id,
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
UserLists,
|
||||
UserListJoinings,
|
||||
} from "@/models/index.js";
|
||||
import { genId, isSelfHost, stringToAcct, toPuny } from "backend-rs";
|
||||
import { genIdAt, isSelfHost, stringToAcct, toPuny } from "backend-rs";
|
||||
import type { DbUserImportJobData } from "@/queue/types.js";
|
||||
import { IsNull } from "typeorm";
|
||||
import { inspect } from "node:util";
|
||||
|
@ -54,9 +54,10 @@ export async function importUserLists(
|
|||
});
|
||||
|
||||
if (list == null) {
|
||||
const now = new Date();
|
||||
list = await UserLists.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
id: genIdAt(now),
|
||||
createdAt: now,
|
||||
userId: user.id,
|
||||
name: listName,
|
||||
}).then((x) => UserLists.findOneByOrFail(x.identifiers[0]));
|
||||
|
|
|
@ -15,7 +15,7 @@ import type { UserPublickey } from "@/models/entities/user-publickey.js";
|
|||
import { verify } from "node:crypto";
|
||||
|
||||
export async function hasSignature(req: IncomingMessage): Promise<string> {
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
const required = meta.secureMode || meta.privateMode;
|
||||
|
||||
try {
|
||||
|
@ -30,7 +30,7 @@ export async function hasSignature(req: IncomingMessage): Promise<string> {
|
|||
}
|
||||
|
||||
export async function checkFetch(req: IncomingMessage): Promise<number> {
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
if (req.headers.host !== config.host) return 400;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { IFlag } from "../../type.js";
|
|||
import { getApIds } from "../../type.js";
|
||||
import { AbuseUserReports, Users } from "@/models/index.js";
|
||||
import { In } from "typeorm";
|
||||
import { genId } from "backend-rs";
|
||||
import { genIdAt } from "backend-rs";
|
||||
|
||||
export default async (
|
||||
actor: CacheableRemoteUser,
|
||||
|
@ -23,9 +23,11 @@ export default async (
|
|||
});
|
||||
if (users.length < 1) return "skip";
|
||||
|
||||
const now = new Date();
|
||||
|
||||
await AbuseUserReports.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
id: genIdAt(now),
|
||||
createdAt: now,
|
||||
targetUserId: users[0].id,
|
||||
targetUserHost: users[0].host,
|
||||
reporterId: actor.id,
|
||||
|
|
|
@ -36,7 +36,7 @@ export async function createImage(
|
|||
|
||||
apLogger.info(`Creating an image: ${image.url}`);
|
||||
|
||||
const instance = await fetchMeta(true);
|
||||
const instance = await fetchMeta();
|
||||
|
||||
let file = await uploadFromUrl({
|
||||
url: image.url,
|
||||
|
|
|
@ -16,7 +16,7 @@ import type { IRemoteUser, CacheableUser } from "@/models/entities/user.js";
|
|||
import { User } from "@/models/entities/user.js";
|
||||
import type { Emoji } from "@/models/entities/emoji.js";
|
||||
import { UserNotePining } from "@/models/entities/user-note-pining.js";
|
||||
import { genId, isSameOrigin, toPuny } from "backend-rs";
|
||||
import { genId, genIdAt, isSameOrigin, toPuny } from "backend-rs";
|
||||
import { UserPublickey } from "@/models/entities/user-publickey.js";
|
||||
import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
|
||||
import { UserProfile } from "@/models/entities/user-profile.js";
|
||||
|
@ -242,16 +242,17 @@ export async function createPerson(
|
|||
|
||||
// Create user
|
||||
let user: IRemoteUser;
|
||||
const now = new Date();
|
||||
try {
|
||||
// Start transaction
|
||||
await db.transaction(async (transactionalEntityManager) => {
|
||||
user = (await transactionalEntityManager.save(
|
||||
new User({
|
||||
id: genId(),
|
||||
id: genIdAt(now),
|
||||
avatarId: null,
|
||||
bannerId: null,
|
||||
createdAt: new Date(),
|
||||
lastFetchedAt: new Date(),
|
||||
createdAt: now,
|
||||
lastFetchedAt: now,
|
||||
name: truncate(person.name, nameLength),
|
||||
isLocked: !!person.manuallyApprovesFollowers,
|
||||
movedToUri: person.movedTo,
|
||||
|
|
|
@ -243,7 +243,7 @@ router.get("/notes/:note", async (ctx, next) => {
|
|||
|
||||
ctx.body = renderActivity(await renderNote(note, false));
|
||||
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
} else {
|
||||
|
@ -273,7 +273,7 @@ router.get("/notes/:note/activity", async (ctx) => {
|
|||
}
|
||||
|
||||
ctx.body = renderActivity(await packActivity(note));
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
} else {
|
||||
|
@ -328,7 +328,7 @@ router.get("/users/:user/publickey", async (ctx) => {
|
|||
|
||||
if (Users.isLocalUser(user)) {
|
||||
ctx.body = renderActivity(renderKey(user, keypair));
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
} else {
|
||||
|
@ -348,7 +348,7 @@ async function userInfo(ctx: Router.RouterContext, user: User | null) {
|
|||
}
|
||||
|
||||
ctx.body = renderActivity(await renderPerson(user as ILocalUser));
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
} else {
|
||||
|
@ -432,7 +432,7 @@ router.get("/emojis/:emoji", async (ctx) => {
|
|||
}
|
||||
|
||||
ctx.body = renderActivity(renderEmoji(emoji));
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
} else {
|
||||
|
@ -464,7 +464,7 @@ router.get("/likes/:like", async (ctx) => {
|
|||
}
|
||||
|
||||
ctx.body = renderActivity(await renderLike(reaction, note));
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
} else {
|
||||
|
@ -502,7 +502,7 @@ router.get(
|
|||
}
|
||||
|
||||
ctx.body = renderActivity(renderFollow(follower, followee));
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
} else {
|
||||
|
@ -545,7 +545,7 @@ router.get("/follows/:followRequestId", async (ctx: Router.RouterContext) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
} else {
|
||||
|
|
|
@ -57,7 +57,7 @@ export default async (ctx: Router.RouterContext) => {
|
|||
|
||||
ctx.body = renderActivity(rendered);
|
||||
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
} else {
|
||||
|
|
|
@ -110,7 +110,7 @@ export default async (ctx: Router.RouterContext) => {
|
|||
ctx.body = renderActivity(rendered);
|
||||
setResponseType(ctx);
|
||||
}
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
} else {
|
||||
|
|
|
@ -110,7 +110,7 @@ export default async (ctx: Router.RouterContext) => {
|
|||
ctx.body = renderActivity(rendered);
|
||||
setResponseType(ctx);
|
||||
}
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
} else {
|
||||
|
|
|
@ -117,7 +117,7 @@ export default async (ctx: Router.RouterContext) => {
|
|||
|
||||
setResponseType(ctx);
|
||||
}
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
} else {
|
||||
|
|
|
@ -84,7 +84,7 @@ export default (endpoint: IEndpoint, ctx: Koa.Context) =>
|
|||
|
||||
// Log IP
|
||||
if (user) {
|
||||
fetchMeta(true).then((meta) => {
|
||||
fetchMeta().then((meta) => {
|
||||
if (!meta.enableIpLogging) return;
|
||||
const ip = ctx.ip;
|
||||
const ips = userIpHistories.get(user.id);
|
||||
|
|
|
@ -117,7 +117,7 @@ export default async (
|
|||
}
|
||||
|
||||
// private mode
|
||||
const meta = await fetchMeta(true);
|
||||
const meta = await fetchMeta();
|
||||
if (
|
||||
meta.privateMode &&
|
||||
ep.meta.requireCredentialPrivateMode &&
|
||||
|
|
|
@ -68,12 +68,10 @@ export async function readUserMessagingMessage(
|
|||
await sendPushNotification(userId, PushNotificationKind.ReadAllChats, {});
|
||||
} else {
|
||||
// そのユーザーとのメッセージで未読がなければイベント発行
|
||||
const hasUnread = await MessagingMessages.exists({
|
||||
where: {
|
||||
userId: otherpartyId,
|
||||
recipientId: userId,
|
||||
isRead: false,
|
||||
},
|
||||
const hasUnread = await MessagingMessages.existsBy({
|
||||
userId: otherpartyId,
|
||||
recipientId: userId,
|
||||
isRead: false,
|
||||
});
|
||||
|
||||
if (!hasUnread) {
|
||||
|
|
|
@ -3,7 +3,7 @@ import type Koa from "koa";
|
|||
import { config } from "@/config.js";
|
||||
import type { ILocalUser } from "@/models/entities/user.js";
|
||||
import { Signins } from "@/models/index.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { genIdAt } from "backend-rs";
|
||||
import { publishMainStream } from "@/services/stream.js";
|
||||
|
||||
export default function (ctx: Koa.Context, user: ILocalUser, redirect = false) {
|
||||
|
@ -29,9 +29,10 @@ export default function (ctx: Koa.Context, user: ILocalUser, redirect = false) {
|
|||
|
||||
(async () => {
|
||||
// Append signin history
|
||||
const now = new Date();
|
||||
const record = await Signins.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
id: genIdAt(now),
|
||||
createdAt: now,
|
||||
userId: user.id,
|
||||
ip: ctx.ip,
|
||||
headers: ctx.headers,
|
||||
|
|
|
@ -3,7 +3,7 @@ import { User } from "@/models/entities/user.js";
|
|||
import { Users, UsedUsernames } from "@/models/index.js";
|
||||
import { UserProfile } from "@/models/entities/user-profile.js";
|
||||
import { IsNull } from "typeorm";
|
||||
import { genId, generateUserToken, hashPassword, toPuny } from "backend-rs";
|
||||
import { genIdAt, generateUserToken, hashPassword, toPuny } from "backend-rs";
|
||||
import { UserKeypair } from "@/models/entities/user-keypair.js";
|
||||
import { UsedUsername } from "@/models/entities/used-username.js";
|
||||
import { db } from "@/db/postgre.js";
|
||||
|
@ -82,19 +82,23 @@ export async function signup(opts: {
|
|||
|
||||
let account!: User;
|
||||
|
||||
const exists = await Users.existsBy({
|
||||
usernameLower: username.toLowerCase(),
|
||||
host: IsNull(),
|
||||
});
|
||||
|
||||
if (exists) {
|
||||
throw new Error("the username is already used");
|
||||
}
|
||||
|
||||
// Start transaction
|
||||
await db.transaction(async (transactionalEntityManager) => {
|
||||
const exist = await transactionalEntityManager.findOneBy(User, {
|
||||
usernameLower: username.toLowerCase(),
|
||||
host: IsNull(),
|
||||
});
|
||||
|
||||
if (exist) throw new Error(" the username is already used");
|
||||
const now = new Date();
|
||||
|
||||
account = await transactionalEntityManager.save(
|
||||
new User({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
id: genIdAt(now),
|
||||
createdAt: now,
|
||||
username: username,
|
||||
usernameLower: username.toLowerCase(),
|
||||
host: host == null ? null : toPuny(host),
|
||||
|
@ -125,7 +129,7 @@ export async function signup(opts: {
|
|||
|
||||
await transactionalEntityManager.save(
|
||||
new UsedUsername({
|
||||
createdAt: new Date(),
|
||||
createdAt: now,
|
||||
username: username.toLowerCase(),
|
||||
}),
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Ads } from "@/models/index.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { genIdAt } from "backend-rs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["admin"],
|
||||
|
@ -32,9 +32,11 @@ export const paramDef = {
|
|||
} as const;
|
||||
|
||||
export default define(meta, paramDef, async (ps) => {
|
||||
const now = new Date();
|
||||
|
||||
await Ads.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
id: genIdAt(now),
|
||||
createdAt: now,
|
||||
expiresAt: new Date(ps.expiresAt),
|
||||
url: ps.url,
|
||||
imageUrl: ps.imageUrl,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Announcements } from "@/models/index.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { genIdAt } from "backend-rs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["admin"],
|
||||
|
@ -74,9 +74,10 @@ export const paramDef = {
|
|||
} as const;
|
||||
|
||||
export default define(meta, paramDef, async (ps) => {
|
||||
const now = new Date();
|
||||
const announcement = await Announcements.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
id: genIdAt(now),
|
||||
createdAt: now,
|
||||
updatedAt: null,
|
||||
title: ps.title,
|
||||
text: ps.text,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import rndstr from "rndstr";
|
||||
import define from "@/server/api/define.js";
|
||||
import { RegistrationTickets } from "@/models/index.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { genIdAt } from "backend-rs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["admin"],
|
||||
|
@ -38,9 +38,11 @@ export default define(meta, paramDef, async () => {
|
|||
chars: "2-9A-HJ-NP-Z", // [0-9A-Z] w/o [01IO] (32 patterns)
|
||||
});
|
||||
|
||||
const now = new Date();
|
||||
|
||||
await RegistrationTickets.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
id: genIdAt(now),
|
||||
createdAt: now,
|
||||
code,
|
||||
});
|
||||
|
||||
|
|
|
@ -470,7 +470,7 @@ export const paramDef = {
|
|||
} as const;
|
||||
|
||||
export default define(meta, paramDef, async () => {
|
||||
const instance = await fetchMeta(false);
|
||||
const instance = await fetchMeta();
|
||||
|
||||
return {
|
||||
maintainerName: instance.maintainerName,
|
||||
|
|
|
@ -40,9 +40,9 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
throw err;
|
||||
});
|
||||
|
||||
const exist = await PromoNotes.exist({ where: { noteId: note.id } });
|
||||
const exists = await PromoNotes.existsBy({ noteId: note.id });
|
||||
|
||||
if (exist) {
|
||||
if (exists) {
|
||||
throw new ApiError(meta.errors.alreadyPromoted);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { fetchMeta, genId } from "backend-rs";
|
||||
import { fetchMeta, genIdAt } from "backend-rs";
|
||||
import { Antennas, UserLists, UserGroupJoinings } from "@/models/index.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { publishInternalEvent } from "@/services/stream.js";
|
||||
|
@ -123,7 +123,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
let userList;
|
||||
let userGroupJoining;
|
||||
|
||||
const instance = await fetchMeta(true);
|
||||
const instance = await fetchMeta();
|
||||
|
||||
const antennas = await Antennas.findBy({
|
||||
userId: user.id,
|
||||
|
@ -152,9 +152,11 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
}
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
|
||||
const antenna = await Antennas.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
id: genIdAt(now),
|
||||
createdAt: now,
|
||||
userId: user.id,
|
||||
name: ps.name,
|
||||
src: ps.src,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Apps } from "@/models/index.js";
|
||||
import { genId, generateSecureRandomString } from "backend-rs";
|
||||
import { genIdAt, generateSecureRandomString } from "backend-rs";
|
||||
import { unique } from "@/prelude/array.js";
|
||||
|
||||
export const meta = {
|
||||
|
@ -48,9 +48,10 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
);
|
||||
|
||||
// Create account
|
||||
const now = new Date();
|
||||
const app = await Apps.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
id: genIdAt(now),
|
||||
createdAt: now,
|
||||
userId: user ? user.id : null,
|
||||
name: ps.name,
|
||||
description: ps.description,
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as crypto from "node:crypto";
|
|||
import define from "@/server/api/define.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { AuthSessions, AccessTokens, Apps } from "@/models/index.js";
|
||||
import { genId, generateSecureRandomString } from "backend-rs";
|
||||
import { genIdAt, generateSecureRandomString } from "backend-rs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["auth"],
|
||||
|
@ -40,14 +40,12 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
const accessToken = generateSecureRandomString(32);
|
||||
|
||||
// Fetch exist access token
|
||||
const exist = await AccessTokens.exists({
|
||||
where: {
|
||||
appId: session.appId,
|
||||
userId: user.id,
|
||||
},
|
||||
const exists = await AccessTokens.existsBy({
|
||||
appId: session.appId,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
if (!exist) {
|
||||
if (!exists) {
|
||||
// Lookup app
|
||||
const app = await Apps.findOneByOrFail({ id: session.appId });
|
||||
|
||||
|
@ -60,7 +58,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
|
||||
// Insert access token doc
|
||||
await AccessTokens.insert({
|
||||
id: genId(),
|
||||
id: genIdAt(now),
|
||||
createdAt: now,
|
||||
lastUsedAt: now,
|
||||
appId: session.appId,
|
||||
|
|
|
@ -3,7 +3,7 @@ import { config } from "@/config.js";
|
|||
import define from "@/server/api/define.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { Apps, AuthSessions } from "@/models/index.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { genIdAt } from "backend-rs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["auth"],
|
||||
|
@ -60,9 +60,10 @@ export default define(meta, paramDef, async (ps) => {
|
|||
const token = uuid();
|
||||
|
||||
// Create session token document
|
||||
const now = new Date();
|
||||
const doc = await AuthSessions.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
id: genIdAt(now),
|
||||
createdAt: now,
|
||||
appId: app.id,
|
||||
token: token,
|
||||
}).then((x) => AuthSessions.findOneByOrFail(x.identifiers[0]));
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue