Merge branch 'develop' into refactor/types

This commit is contained in:
naskya 2024-04-16 22:29:04 +09:00
commit 082948bfe0
No known key found for this signature in database
GPG Key ID: 712D413B3A9FED5C
129 changed files with 1759 additions and 1221 deletions

View File

@ -178,19 +178,13 @@ logLevel: [
# Media Proxy
#mediaProxy: https://example.com/proxy
# Proxy remote files (default: false)
# Proxy remote files (default: true)
#proxyRemoteFiles: true
#allowedPrivateNetworks: [
# '127.0.0.1/32'
#]
# TWA
#twa:
# nameSpace: android_app
# packageName: tld.domain.twa
# sha256CertFingerprints: ['AB:CD:EF']
# Upload or download file size limits (bytes)
#maxFileSize: 262144000

View File

@ -3,30 +3,38 @@
🔒 Found a security vulnerability? [Please disclose it responsibly.](https://firefish.dev/firefish/firefish/-/blob/develop/SECURITY.md)
🤝 By submitting this issue, you agree to follow our [Contribution Guidelines.](https://firefish.dev/firefish/firefish/-/blob/develop/CONTRIBUTING.md) -->
**What happened?** _(Please give us a brief description of what happened.)_
## What happened? <!-- Please give us a brief description of what happened. -->
**What did you expect to happen?** _(Please give us a brief description of what you expected to happen.)_
## What did you expect to happen? <!-- Please give us a brief description of what you expected to happen. -->
**Version** _(What version of firefish is your instance running? You can find this by clicking your instance's logo at the bottom left and then clicking instance information.)_
## Version <!-- What version of firefish is your instance running? You can find this by clicking your instance's logo at the bottom left and then clicking instance information. -->
**Instance** _(What instance of firefish are you using?)_
## What type of issue is this? <!-- If this happens on your device and has to do with the user interface, it's client-side. If this happens on either with the API or the backend, or you got a server-side error in the client, it's server-side. -->
**What type of issue is this?** _(If this happens on your device and has to do with the user interface, it's client-side. If this happens on either with the API or the backend, or you got a server-side error in the client, it's server-side.)_
- [] server-side
- [] client-side
- [] not sure
**What browser are you using? (Client-side issues only)**
<details>
**What operating system are you using? (Client-side issues only)**
### Instance <!-- What instance of firefish are you using? -->
**How do you deploy Firefish on your server? (Server-side issues only)**
### What browser are you using? (client-side issues only)
**What operating system are you using? (Server-side issues only)**
### What operating system are you using? (client-side issues only)
**Relevant log output** _(Please copy and paste any relevant log output. You can find your log by inspecting the page, and going to the "console" tab. This will be automatically formatted into code, so no need for backticks.)_
### How do you deploy Firefish on your server? (server-side issues only)
**Contribution Guidelines**
### What operating system are you using? (Server-side issues only)
### Relevant log output <!-- Please copy and paste any relevant log output. You can find your log by inspecting the page, and going to the "console" tab. -->
</details>
## Contribution Guidelines
By submitting this issue, you agree to follow our [Contribution Guidelines](https://firefish.dev/firefish/firefish/-/blob/develop/CONTRIBUTING.md)
- [ ] I agree to follow this project's Contribution Guidelines
- [ ] I have searched the issue tracker for similar issues, and this is not a duplicate.
**Are you willing to fix this bug?** (optional)
## Are you willing to fix this bug? (optional)
- [ ] Yes. I will fix this bug and open a merge request if the change is agreed upon.

View File

@ -3,18 +3,18 @@
🔒 Found a security vulnerability? [Please disclose it responsibly.](https://firefish.dev/firefish/firefish/-/blob/develop/SECURITY.md)
🤝 By submitting this feature request, you agree to follow our [Contribution Guidelines.](https://firefish.dev/firefish/firefish/-/blob/develop/CONTRIBUTING.md) -->
**What feature would you like implemented?** _(Please give us a brief description of what you'd like.)_
## What feature would you like implemented? <!-- Please give us a brief description of what you'd like. -->
**Why should we add this feature?** _(Please give us a brief description of why your feature is important.)_
## Why should we add this feature? <!-- Please give us a brief description of why your feature is important. -->
**Version** _(What version of firefish is your instance running? You can find this by clicking your instance's logo at the bottom left and then clicking instance information.)_
## Version <!-- What version of firefish is your instance running? You can find this by clicking your instance's logo at the bottom left and then clicking instance information. -->
**Instance** _(What instance of firefish are you using?)_
## Instance <!-- What instance of firefish are you using? -->
**Contribution Guidelines**
## Contribution Guidelines
By submitting this issue, you agree to follow our [Contribution Guidelines](https://firefish.dev/firefish/firefish/-/blob/develop/CONTRIBUTING.md)
- [ ] I agree to follow this project's Contribution Guidelines
- [ ] I have searched the issue tracker for similar requests, and this is not a duplicate.
**Are you willing to implement this feature?** (optional)
## Are you willing to implement this feature? (optional)
- [ ] Yes. I will implement this feature and open a merge request if the change is agreed upon.

View File

@ -1,8 +1,8 @@
<!-- Thanks for taking the time to make Firefish better! It's not required, but please consider using [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) when making your commits. If you use VSCode, please use the [Conventional Commits extension](https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits). -->
**What does this PR do?** _(Please give us a brief description of what this PR does.)_
## What does this PR do? <!-- Please give us a brief description of what this PR does. -->
**Contribution Guidelines**
## Contribution Guidelines
By submitting this merge request, you agree to follow our [Contribution Guidelines](https://firefish.dev/firefish/firefish/-/blob/develop/CONTRIBUTING.md)
- [ ] This change is reviewed in an issue / This is a minor bug fix
- [ ] I agree to follow this project's Contribution Guidelines

View File

@ -12,6 +12,7 @@
"esbenp.prettier-vscode",
"redhat.vscode-yaml",
"yoavbls.pretty-ts-errors",
"biomejs.biome"
"biomejs.biome",
"rust-lang.rust-analyzer"
]
}

207
Cargo.lock generated
View File

@ -59,9 +59,9 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
[[package]]
name = "allocator-api2"
version = "0.2.16"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "android-tzdata"
@ -132,6 +132,18 @@ version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
[[package]]
name = "argon2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
"cpufeatures",
"password-hash",
]
[[package]]
name = "arrayvec"
version = "0.7.4"
@ -190,11 +202,14 @@ checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
name = "backend-rs"
version = "0.0.0"
dependencies = [
"argon2",
"async-trait",
"basen",
"bcrypt",
"cfg-if",
"chrono",
"cuid2",
"emojis",
"idna",
"jsonschema",
"macro_rs",
@ -238,6 +253,12 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
[[package]]
name = "base64ct"
version = "1.6.0"
@ -250,6 +271,19 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dbe4bb73fd931c4d1aaf53b35d1286c8a948ad00ec92c8e3c856f15fd027f43"
[[package]]
name = "bcrypt"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e65938ed058ef47d92cf8b346cc76ef48984572ade631927e9937b5ffc7662c7"
dependencies = [
"base64 0.22.0",
"blowfish",
"getrandom",
"subtle",
"zeroize",
]
[[package]]
name = "bigdecimal"
version = "0.3.1"
@ -303,6 +337,15 @@ dependencies = [
"wyz",
]
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@ -312,6 +355,16 @@ dependencies = [
"generic-array",
]
[[package]]
name = "blowfish"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7"
dependencies = [
"byteorder",
"cipher",
]
[[package]]
name = "borsh"
version = "1.4.0"
@ -384,9 +437,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "cc"
version = "1.0.92"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41"
checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7"
[[package]]
name = "cfg-if"
@ -412,7 +465,17 @@ dependencies = [
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets 0.52.4",
"windows-targets 0.52.5",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
@ -633,13 +696,22 @@ checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
[[package]]
name = "either"
version = "1.10.0"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
dependencies = [
"serde",
]
[[package]]
name = "emojis"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ee61eb945bff65ee7d19d157d39c67c33290ff0742907413fd5eefd29edc979"
dependencies = [
"phf",
]
[[package]]
name = "encoding_rs"
version = "0.8.34"
@ -1075,6 +1147,15 @@ dependencies = [
"syn 2.0.58",
]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"generic-array",
]
[[package]]
name = "ipnet"
version = "2.9.0"
@ -1122,7 +1203,7 @@ checksum = "2a071f4f7efc9a9118dfb627a0a94ef247986e1ab8606a4c806ae2b3aa3b6978"
dependencies = [
"ahash 0.8.11",
"anyhow",
"base64",
"base64 0.21.7",
"bytecount",
"clap",
"fancy-regex",
@ -1175,7 +1256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
dependencies = [
"cfg-if",
"windows-targets 0.52.4",
"windows-targets 0.52.5",
]
[[package]]
@ -1296,9 +1377,9 @@ dependencies = [
[[package]]
name = "napi-build"
version = "2.1.2"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f9130fccc5f763cf2069b34a089a18f0d0883c66aceb81f2fad541a3d823c43"
checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a"
[[package]]
name = "napi-derive"
@ -1350,9 +1431,9 @@ dependencies = [
[[package]]
name = "num"
version = "0.4.1"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41"
dependencies = [
"num-bigint",
"num-complex",
@ -1559,6 +1640,17 @@ dependencies = [
"syn 2.0.58",
]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
name = "paste"
version = "1.0.14"
@ -1580,6 +1672,24 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "phf"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_shared"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
]
[[package]]
name = "pin-project-lite"
version = "0.2.14"
@ -1801,7 +1911,7 @@ version = "0.11.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
dependencies = [
"base64",
"base64 0.21.7",
"bytes",
"encoding_rs",
"futures-core",
@ -1947,7 +2057,7 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
"base64",
"base64 0.21.7",
]
[[package]]
@ -2231,6 +2341,12 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "slab"
version = "0.4.9"
@ -2398,7 +2514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
dependencies = [
"atoi",
"base64",
"base64 0.21.7",
"bigdecimal",
"bitflags 2.5.0",
"byteorder",
@ -2445,7 +2561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
dependencies = [
"atoi",
"base64",
"base64 0.21.7",
"bigdecimal",
"bitflags 2.5.0",
"byteorder",
@ -3041,7 +3157,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.4",
"windows-targets 0.52.5",
]
[[package]]
@ -3059,7 +3175,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.4",
"windows-targets 0.52.5",
]
[[package]]
@ -3079,17 +3195,18 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm 0.52.4",
"windows_aarch64_msvc 0.52.4",
"windows_i686_gnu 0.52.4",
"windows_i686_msvc 0.52.4",
"windows_x86_64_gnu 0.52.4",
"windows_x86_64_gnullvm 0.52.4",
"windows_x86_64_msvc 0.52.4",
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
]
[[package]]
@ -3100,9 +3217,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
@ -3112,9 +3229,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
@ -3124,9 +3241,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
@ -3136,9 +3259,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
@ -3148,9 +3271,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
@ -3160,9 +3283,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
@ -3172,9 +3295,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "winnow"

View File

@ -7,14 +7,17 @@ macro_rs = { path = "packages/macro-rs" }
napi = { version = "2.16.2", default-features = false }
napi-derive = "2.16.2"
napi-build = "2.1.2"
napi-build = "2.1.3"
argon2 = "0.5.3"
async-trait = "0.1.80"
basen = "0.1.0"
bcrypt = "0.15.1"
cfg-if = "1.0.0"
chrono = "0.4.37"
convert_case = "0.6.0"
cuid2 = "0.1.2"
emojis = "0.6.1"
idna = "0.5.0"
jsonschema = "0.17.1"
once_cell = "1.19.0"

View File

@ -2,7 +2,7 @@
Breaking changes are indicated by the :warning: icon.
## Unreleased
## v20240413
- :warning: Removed `patrons` endpoint.

View File

@ -5,9 +5,12 @@ 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
## [v20240413](https://firefish.dev/firefish/firefish/-/merge_requests/10741/commits)
- Add "Media" tab to user page
- Improve federation and rendering of mathematical expressions
- Remove donor information from the web client
- See also: https://info.firefish.dev/notes/9s1n283sb10rh869
- Fix bugs
## [v20240405](https://firefish.dev/firefish/firefish/-/merge_requests/10733/commits)

View File

@ -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
## v20240413
### For all users

View File

@ -2318,3 +2318,4 @@ markLocalFilesNsfwByDefaultDescription: Indépendamment de ce réglage, les util
peuvent supprimer le drapeau « sensible » (NSFW) eux-mêmes. Les fichiers existants
ne sont pas affectés.
noteEditHistory: Historique des publications
media: Multimédia

View File

@ -1,11 +1,11 @@
{
"name": "firefish",
"version": "20240405",
"version": "20240413",
"repository": {
"type": "git",
"url": "https://firefish.dev/firefish/firefish.git"
},
"packageManager": "pnpm@8.15.6",
"packageManager": "pnpm@8.15.7",
"private": true,
"scripts": {
"rebuild": "pnpm run clean && pnpm run build",
@ -36,11 +36,11 @@
"clean-all": "pnpm run clean && pnpm run clean-cargo && pnpm run clean-npm"
},
"dependencies": {
"js-yaml": "4.1.0",
"gulp": "4.0.2",
"gulp-cssnano": "2.1.3",
"gulp-replace": "1.1.4",
"gulp-terser": "2.1.0"
"gulp-terser": "2.1.0",
"js-yaml": "4.1.0"
},
"devDependencies": {
"@biomejs/biome": "1.6.4",
@ -50,7 +50,7 @@
"@biomejs/cli-linux-x64": "^1.6.4",
"@types/node": "20.12.7",
"execa": "8.0.1",
"pnpm": "8.15.6",
"pnpm": "8.15.7",
"typescript": "5.4.5"
}
}

View File

@ -4,6 +4,7 @@ This directory contains all of the packages Firefish uses.
- `backend`: Main backend code written in TypeScript for NodeJS
- `backend-rs`: Backend code written in Rust, bound to NodeJS by [NAPI-RS](https://napi.rs/)
- `macro-rs`: Procedural macros for backend-rs
- `client`: Web interface written in Vue3 and TypeScript
- `sw`: Web [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) written in TypeScript
- `firefish-js`: TypeScript SDK for both backend and client

View File

@ -17,11 +17,14 @@ macro_rs = { workspace = true }
napi = { workspace = true, optional = true, default-features = false, features = ["napi9", "tokio_rt", "chrono_date", "serde-json"] }
napi-derive = { workspace = true, optional = true }
argon2 = { workspace = true, features = ["std"] }
async-trait = { workspace = true }
basen = { workspace = true }
bcrypt = { workspace = true }
cfg-if = { workspace = true }
chrono = { workspace = true }
cuid2 = { workspace = true }
emojis = { workspace = true }
idna = { workspace = true }
jsonschema = { workspace = true }
once_cell = { workspace = true }

View File

@ -3,6 +3,16 @@
/* auto-generated by NAPI-RS */
export interface EnvConfig {
onlyQueue: boolean
onlyServer: boolean
noDaemons: boolean
disableClustering: boolean
verbose: boolean
withLogTime: boolean
slow: boolean
}
export function readEnvironmentConfig(): EnvConfig
export interface ServerConfig {
url: string
port: number
@ -132,9 +142,38 @@ 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
export function sqlLikeEscape(src: string): string
export function safeForSql(src: string): boolean
/** Convert milliseconds to a human readable string */
export function formatMilliseconds(milliseconds: number): string
export 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
export function nyaify(text: string, lang?: string | undefined | null): string
export function hashPassword(password: string): string
export function verifyPassword(password: string, hash: string): boolean
export function isOldPasswordAlgorithm(hash: string): boolean
export interface DecodedReaction {
reaction: string
name: string | null
host: string | null
}
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>
export interface AbuseUserReport {
id: string
createdAt: Date

View File

@ -310,8 +310,9 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}
const { readServerConfig, stringToAcct, acctToString, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, toMastodonId, fromMastodonId, nyaify, AntennaSrcEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initIdGenerator, getTimestamp, genId, secureRndstr } = nativeBinding
const { readEnvironmentConfig, readServerConfig, stringToAcct, acctToString, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, AntennaSrcEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initIdGenerator, getTimestamp, genId, secureRndstr } = nativeBinding
module.exports.readEnvironmentConfig = readEnvironmentConfig
module.exports.readServerConfig = readServerConfig
module.exports.stringToAcct = stringToAcct
module.exports.acctToString = acctToString
@ -321,9 +322,21 @@ module.exports.isSelfHost = isSelfHost
module.exports.isSameOrigin = isSameOrigin
module.exports.extractHost = extractHost
module.exports.toPuny = toPuny
module.exports.isUnicodeEmoji = isUnicodeEmoji
module.exports.sqlLikeEscape = sqlLikeEscape
module.exports.safeForSql = safeForSql
module.exports.formatMilliseconds = formatMilliseconds
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
module.exports.isOldPasswordAlgorithm = isOldPasswordAlgorithm
module.exports.decodeReaction = decodeReaction
module.exports.countReactions = countReactions
module.exports.toDbReaction = toDbReaction
module.exports.AntennaSrcEnum = AntennaSrcEnum
module.exports.MutedNoteReasonEnum = MutedNoteReasonEnum
module.exports.NoteVisibilityEnum = NoteVisibilityEnum

View File

@ -0,0 +1,27 @@
// FIXME: Are these options used?
#[crate::export(object)]
pub struct EnvConfig {
pub only_queue: bool,
pub only_server: bool,
pub no_daemons: bool,
pub disable_clustering: bool,
pub verbose: bool,
pub with_log_time: bool,
pub slow: bool,
}
#[crate::export]
pub fn read_environment_config() -> EnvConfig {
let node_env = std::env::var("NODE_ENV").unwrap_or_default().to_lowercase();
let is_testing = node_env == "test";
EnvConfig {
only_queue: std::env::var("MK_ONLY_QUEUE").is_ok(),
only_server: std::env::var("MK_ONLY_SERVER").is_ok(),
no_daemons: is_testing || std::env::var("MK_NO_DAEMONS").is_ok(),
disable_clustering: is_testing || std::env::var("MK_DISABLE_CLUSTERING").is_ok(),
verbose: std::env::var("MK_VERBOSE").is_ok(),
with_log_time: std::env::var("MK_WITH_LOG_TIME").is_ok(),
slow: std::env::var("MK_SLOW").is_ok(),
}
}

View File

@ -1 +1,2 @@
pub mod environment;
pub mod server;

View File

@ -0,0 +1,31 @@
#[inline]
#[crate::export]
pub fn is_unicode_emoji(s: &str) -> bool {
emojis::get(s).is_some()
}
#[cfg(test)]
mod unit_test {
use super::is_unicode_emoji;
#[test]
fn test_unicode_emoji_check() {
assert!(is_unicode_emoji(""));
assert!(is_unicode_emoji("👍"));
assert!(is_unicode_emoji(""));
assert!(is_unicode_emoji("♥️"));
assert!(is_unicode_emoji("❤️"));
assert!(is_unicode_emoji("💙"));
assert!(is_unicode_emoji("🩷"));
assert!(is_unicode_emoji("🖖🏿"));
assert!(is_unicode_emoji("🏃‍➡️"));
assert!(is_unicode_emoji("👩‍❤️‍👨"));
assert!(is_unicode_emoji("👩‍👦‍👦"));
assert!(is_unicode_emoji("🏳️‍🌈"));
assert!(!is_unicode_emoji("⭐⭐"));
assert!(!is_unicode_emoji("x"));
assert!(!is_unicode_emoji("\t"));
assert!(!is_unicode_emoji(":meow_aww:"));
}
}

View File

@ -0,0 +1,36 @@
#[crate::export]
pub fn sql_like_escape(src: &str) -> String {
src.replace('%', r"\%").replace('_', r"\_")
}
#[crate::export]
pub fn safe_for_sql(src: &str) -> bool {
!src.contains([
'\0', '\x08', '\x09', '\x1a', '\n', '\r', '"', '\'', '\\', '%',
])
}
#[cfg(test)]
mod unit_test {
use super::{safe_for_sql, sql_like_escape};
use pretty_assertions::assert_eq;
#[test]
fn sql_like_escape_test() {
assert_eq!(sql_like_escape(""), "");
assert_eq!(sql_like_escape("abc"), "abc");
assert_eq!(sql_like_escape("a%bc"), r"a\%bc");
assert_eq!(sql_like_escape("a呼%吸bc"), r"a呼\%吸bc");
assert_eq!(sql_like_escape("a呼%吸b%_c"), r"a呼\%吸b\%\_c");
assert_eq!(sql_like_escape("_اللغة العربية"), r"\_اللغة العربية");
}
#[test]
fn safe_for_sql_test() {
assert!(safe_for_sql("123"));
assert!(safe_for_sql("人間"));
assert!(!safe_for_sql("人間\x09"));
assert!(!safe_for_sql("abc\ndef"));
assert!(!safe_for_sql("%something%"));
}
}

View File

@ -0,0 +1,46 @@
/// Convert milliseconds to a human readable string
#[crate::export]
pub fn format_milliseconds(milliseconds: u32) -> String {
let mut seconds = milliseconds / 1000;
let mut minutes = seconds / 60;
let mut hours = minutes / 60;
let days = hours / 24;
seconds %= 60;
minutes %= 60;
hours %= 24;
let mut buf: Vec<String> = vec![];
if days > 0 {
buf.push(format!("{} day(s)", days));
}
if hours > 0 {
buf.push(format!("{} hour(s)", hours));
}
if minutes > 0 {
buf.push(format!("{} minute(s)", minutes));
}
if seconds > 0 {
buf.push(format!("{} second(s)", seconds));
}
buf.join(", ")
}
#[cfg(test)]
mod unit_test {
use super::format_milliseconds;
use pretty_assertions::assert_eq;
#[test]
fn format_milliseconds_test() {
assert_eq!(format_milliseconds(1000), "1 second(s)");
assert_eq!(format_milliseconds(1387938), "23 minute(s), 7 second(s)");
assert_eq!(format_milliseconds(34200457), "9 hour(s), 30 minute(s)");
assert_eq!(
format_milliseconds(998244353),
"11 day(s), 13 hour(s), 17 minute(s), 24 second(s)"
);
}
}

View File

@ -0,0 +1,83 @@
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) {
let _ = CACHE.lock().map(|mut cache| *cache = Some(meta.clone()));
}
#[crate::export]
pub async fn fetch_meta(use_cache: bool) -> Result<Meta, DbErr> {
// try using cache
if use_cache {
if let Some(cache) = CACHE.lock().ok().and_then(|cache| cache.clone()) {
return Ok(cache);
}
}
// try fetching from db
let db = db_conn().await?;
let meta = meta::Entity::find().one(db).await?;
if let Some(meta) = meta {
update_cache(&meta);
return Ok(meta);
}
// create a new meta object and insert into db
let meta = meta::Entity::insert(meta::ActiveModel {
id: ActiveValue::Set("x".to_owned()),
..Default::default()
})
.exec_with_returning(db)
.await?;
update_cache(&meta);
Ok(meta)
}
#[crate::export(object)]
pub struct PugArgs {
pub img: Option<String>,
pub title: String,
pub instance_name: String,
pub desc: Option<String>,
pub icon: Option<String>,
pub splash_icon: Option<String>,
pub theme_color: Option<String>,
pub random_motd: String,
pub private_mode: Option<bool>,
}
#[crate::export]
pub fn meta_to_pug_args(meta: Meta) -> PugArgs {
let mut rng = rand::thread_rng();
let splash_icon = meta
.custom_splash_icons
.choose(&mut rng)
.map(|s| s.to_owned())
.or_else(|| meta.icon_url.to_owned());
let random_motd = meta
.custom_motd
.choose(&mut rng)
.map(|s| s.to_owned())
.unwrap_or_else(|| "Loading...".to_owned());
let name = meta.name.unwrap_or_else(|| "Firefish".to_owned());
PugArgs {
img: meta.banner_url,
title: name.clone(),
instance_name: name.clone(),
desc: meta.description,
icon: meta.icon_url,
splash_icon,
theme_color: meta.theme_color,
random_motd,
private_mode: meta.private_mode,
}
}

View File

@ -1,5 +1,11 @@
pub mod acct;
pub mod check_word_mute;
pub mod convert_host;
pub mod emoji;
pub mod escape_sql;
pub mod format_milliseconds;
pub mod mastodon_id;
pub mod meta;
pub mod nyaify;
pub mod password;
pub mod reaction;

View File

@ -0,0 +1,69 @@
use argon2::{
password_hash,
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
#[crate::export]
pub fn hash_password(password: &str) -> Result<String, password_hash::errors::Error> {
let salt = SaltString::generate(&mut OsRng);
Ok(Argon2::default()
.hash_password(password.as_bytes(), &salt)?
.to_string())
}
#[derive(thiserror::Error, Debug)]
pub enum VerifyError {
#[error("An error occured while bcrypt verification: {0}")]
BcryptError(#[from] bcrypt::BcryptError),
#[error("Invalid argon2 password hash: {0}")]
InvalidArgon2Hash(#[from] password_hash::Error),
#[error("An error occured while argon2 verification: {0}")]
Argon2Error(#[from] argon2::Error),
}
#[crate::export]
pub fn verify_password(password: &str, hash: &str) -> Result<bool, VerifyError> {
if is_old_password_algorithm(hash) {
Ok(bcrypt::verify(password, hash)?)
} else {
let parsed_hash = PasswordHash::new(hash)?;
Ok(Argon2::default()
.verify_password(password.as_bytes(), &parsed_hash)
.is_ok())
}
}
#[inline]
#[crate::export]
pub fn is_old_password_algorithm(hash: &str) -> bool {
// bcrypt hashes start with $2[ab]$
hash.starts_with("$2")
}
#[cfg(test)]
mod unit_test {
use super::{hash_password, is_old_password_algorithm, verify_password};
#[test]
fn verify_password_test() {
let password = "omWc*%sD^fn7o2cXmc9e2QasBdrbRuhNB*gx!J5";
let hash = hash_password(password).unwrap();
assert!(verify_password(password, hash.as_str()).unwrap());
let argon2_hash = "$argon2id$v=19$m=19456,t=2,p=1$jty3puDFd4ENv/lgHn3ROQ$kRHDdEoVv2rruvnF731E74NxnYlvj5FMgePdGIIq3Jk";
let argon2_invalid_hash = "$argon2id$v=19$m=19456,t=2,p=1$jty3puDFd4ENv/lgHn3ROQ$kRHDdEoVv2rruvnF731E74NxnYlvj4FMgePdGIIq3Jk";
let bcrypt_hash = "$2a$12$WzUc.20jgbHmQjUMqTr8vOhKqYbS1BUvubapv/GLjCK1IN.h4e4la";
let bcrypt_invalid_hash = "$2a$12$WzUc.20jgbHmQjUMqTr7vOhKqYbS1BUvubapv/GLjCK1IN.h4e4la";
assert!(!is_old_password_algorithm(argon2_hash));
assert!(is_old_password_algorithm(bcrypt_hash));
assert!(verify_password(password, argon2_hash).unwrap());
assert!(verify_password(password, bcrypt_hash).unwrap());
assert!(!verify_password(password, argon2_invalid_hash).unwrap());
assert!(!verify_password(password, bcrypt_invalid_hash).unwrap());
}
}

View File

@ -0,0 +1,191 @@
use crate::database::db_conn;
use crate::misc::{convert_host::to_puny, emoji::is_unicode_emoji, meta::fetch_meta};
use crate::model::entity::emoji;
use once_cell::sync::Lazy;
use regex::Regex;
use sea_orm::prelude::*;
use std::collections::HashMap;
#[derive(PartialEq, Debug)]
#[crate::export(object)]
pub struct DecodedReaction {
pub reaction: String,
pub name: Option<String>,
pub host: Option<String>,
}
#[crate::export]
pub fn decode_reaction(reaction: &str) -> DecodedReaction {
// Misskey allows you to include "+" and "-" in emoji shortcodes
// MFM spec: https://github.com/misskey-dev/mfm.js/blob/6aaf68089023c6adebe44123eebbc4dcd75955e0/docs/syntax.md?plain=1#L583
// Misskey's implementation: https://github.com/misskey-dev/misskey/blob/bba3097765317cbf95d09627961b5b5dce16a972/packages/backend/src/core/ReactionService.ts#L68
static RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^:([0-9A-Za-z_+-]+)(?:@([0-9A-Za-z_.-]+))?:$").unwrap());
if let Some(captures) = RE.captures(reaction) {
let name = &captures[1];
let host = captures.get(2).map(|s| s.as_str());
DecodedReaction {
reaction: format!(":{}@{}:", name, host.unwrap_or(".")),
name: Some(name.to_owned()),
host: host.map(|s| s.to_owned()),
}
} else {
DecodedReaction {
reaction: reaction.to_owned(),
name: None,
host: None,
}
}
}
#[crate::export]
pub fn count_reactions(reactions: &HashMap<String, u32>) -> HashMap<String, u32> {
let mut res = HashMap::<String, u32>::new();
for (reaction, count) in reactions.iter() {
if count > &0 {
let decoded = decode_reaction(reaction).reaction;
let total = res.entry(decoded).or_insert(0);
*total += count;
}
}
res
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Idna error: {0}")]
IdnaError(#[from] idna::Errors),
#[error("Database error: {0}")]
DbError(#[from] DbErr),
}
#[crate::export]
pub async fn to_db_reaction(reaction: Option<&str>, host: Option<&str>) -> Result<String, Error> {
if let Some(reaction) = reaction {
// FIXME: Is it okay to do this only here?
// This was introduced in https://firefish.dev/firefish/firefish/-/commit/af730e75b6fc1a57ca680ce83459d7e433b130cf
if reaction.contains('❤') || reaction.contains("♥️") {
return Ok("❤️".to_owned());
}
if is_unicode_emoji(reaction) {
return Ok(reaction.to_owned());
}
static RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^:([0-9A-Za-z_+-]+)(?:@\.)?:$").unwrap());
if let Some(captures) = RE.captures(reaction) {
let name = &captures[1];
let db = db_conn().await?;
if let Some(host) = host {
// remote emoji
let ascii_host = to_puny(host)?;
// TODO: Does SeaORM have the `exists` method?
if emoji::Entity::find()
.filter(emoji::Column::Name.eq(name))
.filter(emoji::Column::Host.eq(&ascii_host))
.one(db)
.await?
.is_some()
{
return Ok(format!(":{name}@{ascii_host}:"));
}
} else {
// local emoji
// TODO: Does SeaORM have the `exists` method?
if emoji::Entity::find()
.filter(emoji::Column::Name.eq(name))
.filter(emoji::Column::Host.is_null())
.one(db)
.await?
.is_some()
{
return Ok(format!(":{name}:"));
}
}
};
};
Ok(fetch_meta(true).await?.default_reaction)
}
#[cfg(test)]
mod unit_test {
use super::{decode_reaction, DecodedReaction};
use pretty_assertions::{assert_eq, assert_ne};
#[test]
fn test_decode_reaction() {
let unicode_emoji_1 = DecodedReaction {
reaction: "".to_string(),
name: None,
host: None,
};
let unicode_emoji_2 = DecodedReaction {
reaction: "🩷".to_string(),
name: None,
host: None,
};
assert_eq!(decode_reaction(""), unicode_emoji_1);
assert_eq!(decode_reaction("🩷"), unicode_emoji_2);
assert_ne!(decode_reaction(""), unicode_emoji_2);
assert_ne!(decode_reaction("🩷"), unicode_emoji_1);
let unicode_emoji_3 = DecodedReaction {
reaction: "🖖🏿".to_string(),
name: None,
host: None,
};
assert_eq!(decode_reaction("🖖🏿"), unicode_emoji_3);
let local_emoji = DecodedReaction {
reaction: ":meow_melt_tears@.:".to_string(),
name: Some("meow_melt_tears".to_string()),
host: None,
};
assert_eq!(decode_reaction(":meow_melt_tears:"), local_emoji);
let remote_emoji_1 = DecodedReaction {
reaction: ":meow_uwu@some-domain.example.org:".to_string(),
name: Some("meow_uwu".to_string()),
host: Some("some-domain.example.org".to_string()),
};
assert_eq!(
decode_reaction(":meow_uwu@some-domain.example.org:"),
remote_emoji_1
);
let remote_emoji_2 = DecodedReaction {
reaction: ":C++23@xn--eckwd4c7c.example.org:".to_string(),
name: Some("C++23".to_string()),
host: Some("xn--eckwd4c7c.example.org".to_string()),
};
assert_eq!(
decode_reaction(":C++23@xn--eckwd4c7c.example.org:"),
remote_emoji_2
);
let invalid_reaction_1 = DecodedReaction {
reaction: ":foo".to_string(),
name: None,
host: None,
};
assert_eq!(decode_reaction(":foo"), invalid_reaction_1);
let invalid_reaction_2 = DecodedReaction {
reaction: ":foo&@example.com:".to_string(),
name: None,
host: None,
};
assert_eq!(decode_reaction(":foo&@example.com:"), invalid_reaction_2);
}
}

View File

@ -22,9 +22,9 @@
"@swc/core-android-arm64": "1.3.11"
},
"dependencies": {
"@bull-board/api": "5.15.3",
"@bull-board/koa": "5.15.3",
"@bull-board/ui": "5.15.3",
"@bull-board/api": "5.15.5",
"@bull-board/koa": "5.15.5",
"@bull-board/ui": "5.15.5",
"@discordapp/twemoji": "^15.0.3",
"@koa/cors": "5.0.0",
"@koa/multer": "3.0.2",
@ -33,15 +33,12 @@
"@peertube/http-signature": "1.7.0",
"@redocly/openapi-core": "1.11.0",
"@sinonjs/fake-timers": "11.2.2",
"@twemoji/parser": "^15.1.1",
"adm-zip": "0.5.10",
"ajv": "8.12.0",
"archiver": "7.0.1",
"argon2": "^0.40.1",
"aws-sdk": "2.1597.0",
"aws-sdk": "2.1599.0",
"axios": "^1.6.8",
"backend-rs": "workspace:*",
"bcryptjs": "2.4.3",
"blurhash": "2.0.5",
"bull": "4.12.2",
"cacheable-lookup": "TheEssem/cacheable-lookup",
@ -54,7 +51,7 @@
"date-fns": "3.6.0",
"decompress": "^4.2.1",
"deep-email-validator": "0.1.21",
"deepl-node": "1.12.0",
"deepl-node": "1.13.0",
"escape-regexp": "0.0.1",
"feed": "4.2.2",
"file-type": "19.0.0",
@ -100,7 +97,7 @@
"punycode": "2.3.1",
"pureimage": "0.4.13",
"qrcode": "1.5.3",
"qs": "6.12.0",
"qs": "6.12.1",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"redis-semaphore": "5.5.1",
@ -130,7 +127,6 @@
"@swc/cli": "0.3.12",
"@swc/core": "1.4.13",
"@types/adm-zip": "^0.5.5",
"@types/bcryptjs": "2.4.6",
"@types/color-convert": "^2.0.3",
"@types/content-disposition": "^0.5.8",
"@types/escape-regexp": "0.0.3",

View File

@ -3,7 +3,7 @@ import chalk from "chalk";
import Xev from "xev";
import Logger from "@/services/logger.js";
import { envOption } from "../env.js";
import { envOption } from "@/config/index.js";
import { inspect } from "node:util";
// for typeorm
@ -76,9 +76,7 @@ cluster.on("exit", (worker) => {
});
// Display detail of unhandled promise rejection
if (!envOption.quiet) {
process.on("unhandledRejection", console.dir);
}
process.on("unhandledRejection", console.dir);
// Display detail of uncaught exception
process.on("uncaughtException", (err) => {

View File

@ -10,7 +10,7 @@ import semver from "semver";
import Logger from "@/services/logger.js";
import loadConfig from "@/config/load.js";
import type { Config } from "@/config/types.js";
import { envOption } from "@/env.js";
import { envOption } from "@/config/index.js";
import { showMachineInfo } from "@/misc/show-machine-info.js";
import { db, initDb } from "@/db/postgre.js";
import { inspect } from "node:util";
@ -28,58 +28,56 @@ const bootLogger = logger.createSubLogger("boot", "magenta", false);
const themeColor = chalk.hex("#31748f");
function greet() {
if (!envOption.quiet) {
//#region Firefish logo
console.log(
themeColor(
"██████╗ ██╗██████╗ ███████╗███████╗██╗███████╗██╗ ██╗ ○ ▄ ▄ ",
),
);
console.log(
themeColor(
"██╔════╝██║██╔══██╗██╔════╝██╔════╝██║██╔════╝██║ ██║ ⚬ █▄▄ █▄▄ ",
),
);
console.log(
themeColor(
"█████╗ ██║██████╔╝█████╗ █████╗ ██║███████╗███████║ ▄▄▄▄▄▄ ▄ ",
),
);
console.log(
themeColor(
"██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║╚════██║██╔══██║ █ █ █▄▄ ",
),
);
console.log(
themeColor(
"██║ ██║██║ ██║███████╗██║ ██║███████║██║ ██║ █ ● ● █ ",
),
);
console.log(
themeColor(
"╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ▀▄▄▄▄▄▄▀ ",
),
);
//#endregion
//#region Firefish logo
console.log(
themeColor(
"██████╗ ██╗██████╗ ███████╗███████╗██╗███████╗██╗ ██╗ ○ ▄ ▄ ",
),
);
console.log(
themeColor(
"██╔════╝██║██╔══██╗██╔════╝██╔════╝██║██╔════╝██║ ██║ ⚬ █▄▄ █▄▄ ",
),
);
console.log(
themeColor(
"█████╗ ██║██████╔╝█████╗ █████╗ ██║███████╗███████║ ▄▄▄▄▄▄ ▄ ",
),
);
console.log(
themeColor(
"██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║╚════██║██╔══██║ █ █ █▄▄ ",
),
);
console.log(
themeColor(
"██║ ██║██║ ██║███████╗██║ ██║███████║██║ ██║ █ ● ● █ ",
),
);
console.log(
themeColor(
"╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ▀▄▄▄▄▄▄▀ ",
),
);
//#endregion
console.log(
" Firefish is an open-source decentralized microblogging platform.",
);
console.log(
chalk.rgb(
255,
136,
0,
)(
" If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish",
),
);
console.log(
" Firefish is an open-source decentralized microblogging platform.",
);
console.log(
chalk.rgb(
255,
136,
0,
)(
" If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish",
),
);
console.log("");
console.log(
chalkTemplate`--- ${os.hostname()} {gray (PID: ${process.pid.toString()})} ---`,
);
}
console.log("");
console.log(
chalkTemplate`--- ${os.hostname()} {gray (PID: ${process.pid.toString()})} ---`,
);
bootLogger.info("Welcome to Firefish!");
bootLogger.info(`Firefish v${meta.version}`, null, true);

View File

@ -1,3 +1,5 @@
import load from "./load.js";
import { readEnvironmentConfig } from "backend-rs";
export default load();
export const envOption = readEnvironmentConfig();

View File

@ -55,6 +55,7 @@ export default function load() {
mixin.userAgent = `Firefish/${meta.version} (${config.url})`;
mixin.clientEntry = clientManifest["src/init.ts"];
if (config.proxyRemoteFiles == null) config.proxyRemoteFiles = true;
if (!config.redis.prefix) config.redis.prefix = mixin.hostname;
if (config.cacheServer && !config.cacheServer.prefix)
config.cacheServer.prefix = mixin.hostname;

View File

@ -1,7 +1,7 @@
import si from "systeminformation";
import Xev from "xev";
import * as osUtils from "os-utils";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
const ev = new Xev();
@ -20,7 +20,7 @@ export default function () {
ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length || 50));
});
fetchMeta().then((meta) => {
fetchMeta(true).then((meta) => {
if (!meta.enableServerMachineStats) return;
});

View File

@ -1,25 +0,0 @@
const envOption = {
onlyQueue: false,
onlyServer: false,
noDaemons: false,
disableClustering: false,
verbose: false,
withLogTime: false,
quiet: false,
slow: false,
};
for (const key of Object.keys(envOption) as (keyof typeof envOption)[]) {
if (
process.env[
`MK_${key.replace(/[A-Z]/g, (letter) => `_${letter}`).toUpperCase()}`
]
)
envOption[key] = true;
}
if (process.env.NODE_ENV === "test") envOption.disableClustering = true;
if (process.env.NODE_ENV === "test") envOption.quiet = true;
if (process.env.NODE_ENV === "test") envOption.noDaemons = true;
export { envOption };

View File

@ -1,17 +0,0 @@
export function convertMilliseconds(ms: number) {
let seconds = Math.round(ms / 1000);
let minutes = Math.round(seconds / 60);
let hours = Math.round(minutes / 60);
const days = Math.round(hours / 24);
seconds %= 60;
minutes %= 60;
hours %= 24;
const result = [];
if (days > 0) result.push(`${days} day(s)`);
if (hours > 0) result.push(`${hours} hour(s)`);
if (minutes > 0) result.push(`${minutes} minute(s)`);
if (seconds > 0) result.push(`${seconds} second(s)`);
return result.join(", ");
}

View File

@ -1,5 +0,0 @@
import twemoji from "@twemoji/parser/dist/lib/regex.js";
const twemojiRegex = twemoji.default;
export const emojiRegex = new RegExp(`(${twemojiRegex.source})`);
export const emojiRegexAtStartToEnd = new RegExp(`^(${twemojiRegex.source})$`);

View File

@ -1,72 +0,0 @@
import { db } from "@/db/postgre.js";
import { Meta } from "@/models/entities/meta.js";
let cache: Meta;
export function metaToPugArgs(meta: Meta): object {
let motd = ["Loading..."];
if (meta.customMotd.length > 0) {
motd = meta.customMotd;
}
let splashIconUrl = meta.iconUrl;
if (meta.customSplashIcons.length > 0) {
splashIconUrl =
meta.customSplashIcons[
Math.floor(Math.random() * meta.customSplashIcons.length)
];
}
return {
img: meta.bannerUrl,
title: meta.name || "Firefish",
instanceName: meta.name || "Firefish",
desc: meta.description,
icon: meta.iconUrl,
splashIcon: splashIconUrl,
themeColor: meta.themeColor,
randomMOTD: motd[Math.floor(Math.random() * motd.length)],
privateMode: meta.privateMode,
};
}
export async function fetchMeta(noCache = false): Promise<Meta> {
if (!noCache && cache) return cache;
return await db.transaction(async (transactionalEntityManager) => {
// New IDs are prioritized because multiple records may have been created due to past bugs.
const metas = await transactionalEntityManager.find(Meta, {
order: {
id: "DESC",
},
});
const meta = metas[0];
if (meta) {
cache = meta;
return meta;
} else {
// If fetchMeta is called at the same time when meta is empty, this part may be called at the same time, so use fail-safe upsert.
const saved = await transactionalEntityManager
.upsert(
Meta,
{
id: "x",
},
["id"],
)
.then((x) =>
transactionalEntityManager.findOneByOrFail(Meta, x.identifiers[0]),
);
cache = saved;
return saved;
}
});
}
setInterval(() => {
fetchMeta(true).then((meta) => {
cache = meta;
});
}, 1000 * 10);

View File

@ -1,9 +1,9 @@
import { fetchMeta } from "./fetch-meta.js";
import { fetchMeta } from "backend-rs";
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();
const meta = await fetchMeta(true);
if (meta.proxyAccountId == null) return null;
return (await Users.findOneByOrFail({
id: meta.proxyAccountId,

View File

@ -1,20 +0,0 @@
import bcrypt from "bcryptjs";
import * as argon2 from "argon2";
export async function hashPassword(password: string): Promise<string> {
return argon2.hash(password);
}
export async function comparePassword(
password: string,
hash: string,
): Promise<boolean> {
if (isOldAlgorithm(hash)) return bcrypt.compare(password, hash);
return argon2.verify(hash, password);
}
export function isOldAlgorithm(hash: string): boolean {
// bcrypt hashes start with $2[ab]$
return hash.startsWith("$2");
}

View File

@ -3,8 +3,7 @@ import { Emojis } from "@/models/index.js";
import type { Emoji } from "@/models/entities/emoji.js";
import type { Note } from "@/models/entities/note.js";
import { Cache } from "./cache.js";
import { isSelfHost, toPuny } from "backend-rs";
import { decodeReaction } from "./reaction-lib.js";
import { decodeReaction, isSelfHost, toPuny } from "backend-rs";
import config from "@/config/index.js";
import { query } from "@/prelude/url.js";
import { redisClient } from "@/db/redis.js";

View File

@ -12,7 +12,7 @@ export function parse(acct: any): Post {
cw: acct.cw,
localOnly: acct.localOnly,
createdAt: new Date(acct.createdAt),
visibility: "hidden" + (acct.visibility || ""),
visibility: `hidden${acct.visibility || ""}`,
};
}

View File

@ -1,87 +0,0 @@
import { emojiRegex } from "./emoji-regex.js";
import { fetchMeta } from "./fetch-meta.js";
import { Emojis } from "@/models/index.js";
import { toPuny } from "backend-rs";
import { IsNull } from "typeorm";
export function convertReactions(reactions: Record<string, number>) {
const result = new Map();
for (const reaction in reactions) {
if (reactions[reaction] <= 0) continue;
const decoded = decodeReaction(reaction).reaction;
result.set(decoded, (result.get(decoded) || 0) + reactions[reaction]);
}
return Object.fromEntries(result);
}
export async function toDbReaction(
reaction?: string | null,
reacterHost?: string | null,
): Promise<string> {
if (!reaction) return (await fetchMeta()).defaultReaction;
reacterHost = reacterHost == null ? null : toPuny(reacterHost);
if (reaction.includes("❤") || reaction.includes("♥️")) return "❤️";
// Allow unicode reactions
const match = emojiRegex.exec(reaction);
if (match) {
const unicode = match[0];
return unicode;
}
const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/);
if (custom) {
const name = custom[1];
const emoji = await Emojis.findOneBy({
host: reacterHost || IsNull(),
name,
});
if (emoji) return reacterHost ? `:${name}@${reacterHost}:` : `:${name}:`;
}
return (await fetchMeta()).defaultReaction;
}
type DecodedReaction = {
/**
* (Unicode Emoji or ':name@hostname' or ':name@.')
*/
reaction: string;
/**
* name (name, Emojiクエリに使う)
*/
name?: string;
/**
* host (host, Emojiクエリに使う)
*/
host?: string | null;
};
export function decodeReaction(str: string): DecodedReaction {
const custom = str.match(/^:([\w+-]+)(?:@([\w.-]+))?:$/);
if (custom) {
const name = custom[1];
const host = custom[2] || null;
return {
reaction: `:${name}@${host || "."}:`, // ローカル分は@以降を省略するのではなく.にする
name,
host,
};
}
return {
reaction: str,
name: undefined,
host: undefined,
};
}

View File

@ -1,3 +0,0 @@
export function safeForSql(text: string): boolean {
return !/[\0\x08\x09\x1a\n\r"'\\\%]/g.test(text);
}

View File

@ -1,4 +1,4 @@
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import type { Instance } from "@/models/entities/instance.js";
import type { Meta } from "@/models/entities/meta.js";
@ -13,7 +13,7 @@ export async function shouldBlockInstance(
host: Instance["host"],
meta?: Meta,
): Promise<boolean> {
const { blockedHosts } = meta ?? (await fetchMeta());
const { blockedHosts } = meta ?? (await fetchMeta(true));
return blockedHosts.some(
(blockedHost) => host === blockedHost || host.endsWith(`.${blockedHost}`),
);
@ -30,7 +30,7 @@ export async function shouldSilenceInstance(
host: Instance["host"],
meta?: Meta,
): Promise<boolean> {
const { silencedHosts } = meta ?? (await fetchMeta());
const { silencedHosts } = meta ?? (await fetchMeta(true));
return silencedHosts.some(
(silencedHost) =>
host === silencedHost || host.endsWith(`.${silencedHost}`),

View File

@ -1,5 +1,5 @@
import { Brackets } from "typeorm";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { Instances } from "@/models/index.js";
import type { Instance } from "@/models/entities/instance.js";
import { DAY } from "@/const.js";
@ -19,7 +19,7 @@ export async function skippedInstances(
hosts: Instance["host"][],
): Promise<Instance["host"][]> {
// first check for blocked instances since that info may already be in memory
const meta = await fetchMeta();
const meta = await fetchMeta(true);
const shouldSkip = await Promise.all(
hosts.map((host) => shouldBlockInstance(host, meta)),
);

View File

@ -1,3 +0,0 @@
export function sqlLikeEscape(s: string) {
return s.replace(/([%_])/g, "\\$1");
}

View File

@ -1,7 +1,7 @@
import fetch from "node-fetch";
import { Converter } from "opencc-js";
import { getAgentByUrl } from "@/misc/fetch.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import type { PostLanguage } from "@/misc/langmap";
import * as deepl from "deepl-node";
@ -26,7 +26,7 @@ export async function translate(
from: PostLanguage | null,
to: PostLanguage,
) {
const instance = await fetchMeta();
const instance = await fetchMeta(true);
if (instance.deeplAuthKey == null && instance.libreTranslateApiUrl == null) {
throw Error("No translator is set up on this server.");

View File

@ -2,7 +2,7 @@ import { db } from "@/db/postgre.js";
import { NoteReaction } from "@/models/entities/note-reaction.js";
import { Notes, Users } from "../index.js";
import type { Packed } from "@/misc/schema.js";
import { decodeReaction } from "@/misc/reaction-lib.js";
import { decodeReaction } from "backend-rs";
import type { User } from "@/models/entities/user.js";
export const NoteReactionRepository = db.getRepository(NoteReaction).extend({

View File

@ -12,9 +12,8 @@ import {
Channels,
} from "../index.js";
import type { Packed } from "@/misc/schema.js";
import { nyaify } from "backend-rs";
import { countReactions, decodeReaction, nyaify } from "backend-rs";
import { awaitAll } from "@/prelude/await-all.js";
import { convertReactions, decodeReaction } from "@/misc/reaction-lib.js";
import type { NoteReaction } from "@/models/entities/note-reaction.js";
import {
aggregateNoteEmojis,
@ -214,7 +213,7 @@ export const NoteRepository = db.getRepository(Note).extend({
note.visibility === "specified" ? note.visibleUserIds : undefined,
renoteCount: note.renoteCount,
repliesCount: note.repliesCount,
reactions: convertReactions(note.reactions),
reactions: countReactions(note.reactions),
reactionEmojis: reactionEmoji,
emojis: noteEmoji,
tags: note.tags.length > 0 ? note.tags : undefined,

View File

@ -5,7 +5,7 @@ import config from "@/config/index.js";
import type { DriveFile } from "@/models/entities/drive-file.js";
import type { IActivity } from "@/remote/activitypub/type.js";
import type { Webhook, webhookEventTypes } from "@/models/entities/webhook.js";
import { envOption } from "../env.js";
import { envOption } from "@/config/index.js";
import processDeliver from "./processors/deliver.js";
import processInbox from "./processors/inbox.js";

View File

@ -5,7 +5,7 @@ import perform from "@/remote/activitypub/perform.js";
import Logger from "@/services/logger.js";
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
import { Instances } from "@/models/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { toPuny, extractHost } from "backend-rs";
import { getApId } from "@/remote/activitypub/type.js";
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
@ -41,7 +41,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
const host = toPuny(new URL(signature.keyId).hostname);
// interrupt if blocked
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (await shouldBlockInstance(host, meta)) {
return `Blocked request: ${host}`;
}

View File

@ -1,7 +1,7 @@
import { URL } from "url";
import httpSignature, { IParsedSignature } from "@peertube/http-signature";
import config from "@/config/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { toPuny } from "backend-rs";
import DbResolver from "@/remote/activitypub/db-resolver.js";
import { getApId } from "@/remote/activitypub/type.js";
@ -12,7 +12,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();
const meta = await fetchMeta(true);
const required = meta.secureMode || meta.privateMode;
try {
@ -27,7 +27,7 @@ export async function hasSignature(req: IncomingMessage): Promise<string> {
}
export async function checkFetch(req: IncomingMessage): Promise<number> {
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
if (req.headers.host !== config.host) return 400;

View File

@ -1,5 +1,5 @@
import type { CacheableRemoteUser } from "@/models/entities/user.js";
import deleteNode from "@/services/note/delete.js";
import deleteNote from "@/services/note/delete.js";
import { apLogger } from "../../logger.js";
import DbResolver from "../../db-resolver.js";
import { getApLock } from "@/misc/app-lock.js";
@ -36,7 +36,7 @@ export default async function (
return "The user trying to delete the post is not the post author";
}
await deleteNode(actor, note);
await deleteNote(actor, note);
return "ok: note deleted";
} finally {
await lock.release();

View File

@ -1,7 +1,7 @@
import { uploadFromUrl } from "@/services/drive/upload-from-url.js";
import type { CacheableRemoteUser } from "@/models/entities/user.js";
import Resolver from "../resolver.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { apLogger } from "../logger.js";
import type { DriveFile } from "@/models/entities/drive-file.js";
import { DriveFiles } from "@/models/index.js";
@ -34,7 +34,7 @@ export async function createImage(
logger.info(`Creating the Image: ${image.url}`);
const instance = await fetchMeta();
const instance = await fetchMeta(true);
let file = await uploadFromUrl({
url: image.url,

View File

@ -1,7 +1,7 @@
import config from "@/config/index.js";
import type { ILocalUser } from "@/models/entities/user.js";
import { getInstanceActor } from "@/services/instance-actor.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { extractHost, isSelfHost } from "backend-rs";
import { apGet } from "./request.js";
import type { IObject, ICollection, IOrderedCollection } from "./type.js";
@ -100,7 +100,7 @@ export default class Resolver {
return await this.resolveLocal(value);
}
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (await shouldBlockInstance(host, meta)) {
throw new Error("Instance is blocked");
}

View File

@ -9,7 +9,7 @@ import renderKey from "@/remote/activitypub/renderer/key.js";
import { renderPerson } from "@/remote/activitypub/renderer/person.js";
import renderEmoji from "@/remote/activitypub/renderer/emoji.js";
import { inbox as processInbox } from "@/queue/index.js";
import { isSelfHost } from "backend-rs";
import { fetchMeta, isSelfHost } from "backend-rs";
import {
Notes,
Users,
@ -25,7 +25,6 @@ import {
getSignatureUser,
} from "@/remote/activitypub/check-fetch.js";
import { getInstanceActor } from "@/services/instance-actor.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import renderFollow from "@/remote/activitypub/renderer/follow.js";
import Featured from "./activitypub/featured.js";
import Following from "./activitypub/following.js";
@ -238,7 +237,7 @@ router.get("/notes/:note", async (ctx, next) => {
ctx.body = renderActivity(await renderNote(note, false));
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
@ -268,7 +267,7 @@ router.get("/notes/:note/activity", async (ctx) => {
}
ctx.body = renderActivity(await packActivity(note));
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
@ -323,7 +322,7 @@ router.get("/users/:user/publickey", async (ctx) => {
if (Users.isLocalUser(user)) {
ctx.body = renderActivity(renderKey(user, keypair));
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
@ -343,7 +342,7 @@ async function userInfo(ctx: Router.RouterContext, user: User | null) {
}
ctx.body = renderActivity(await renderPerson(user as ILocalUser));
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
@ -426,8 +425,8 @@ router.get("/emojis/:emoji", async (ctx) => {
return;
}
ctx.body = renderActivity(await renderEmoji(emoji));
const meta = await fetchMeta();
ctx.body = renderActivity(renderEmoji(emoji));
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
@ -459,7 +458,7 @@ router.get("/likes/:like", async (ctx) => {
}
ctx.body = renderActivity(await renderLike(reaction, note));
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
@ -497,7 +496,7 @@ router.get(
}
ctx.body = renderActivity(renderFollow(follower, followee));
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
@ -540,7 +539,7 @@ router.get("/follows/:followRequestId", async (ctx: Router.RouterContext) => {
return;
}
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {

View File

@ -5,7 +5,7 @@ import renderOrderedCollection from "@/remote/activitypub/renderer/ordered-colle
import renderNote from "@/remote/activitypub/renderer/note.js";
import { Users, Notes, UserNotePinings } from "@/models/index.js";
import { checkFetch } from "@/remote/activitypub/check-fetch.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { setResponseType } from "../activitypub.js";
import type Router from "@koa/router";
@ -57,7 +57,7 @@ export default async (ctx: Router.RouterContext) => {
ctx.body = renderActivity(rendered);
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {

View File

@ -8,7 +8,7 @@ import renderFollowUser from "@/remote/activitypub/renderer/follow-user.js";
import { Users, Followings, UserProfiles } from "@/models/index.js";
import type { Following } from "@/models/entities/following.js";
import { checkFetch } from "@/remote/activitypub/check-fetch.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { setResponseType } from "../activitypub.js";
import type { FindOptionsWhere } from "typeorm";
import type Router from "@koa/router";
@ -110,7 +110,7 @@ export default async (ctx: Router.RouterContext) => {
ctx.body = renderActivity(rendered);
setResponseType(ctx);
}
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {

View File

@ -8,7 +8,7 @@ import renderFollowUser from "@/remote/activitypub/renderer/follow-user.js";
import { Users, Followings, UserProfiles } from "@/models/index.js";
import type { Following } from "@/models/entities/following.js";
import { checkFetch } from "@/remote/activitypub/check-fetch.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { setResponseType } from "../activitypub.js";
import type { FindOptionsWhere } from "typeorm";
import type Router from "@koa/router";
@ -110,7 +110,7 @@ export default async (ctx: Router.RouterContext) => {
ctx.body = renderActivity(rendered);
setResponseType(ctx);
}
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {

View File

@ -11,7 +11,7 @@ import * as url from "@/prelude/url.js";
import { Users, Notes } from "@/models/index.js";
import type { Note } from "@/models/entities/note.js";
import { checkFetch } from "@/remote/activitypub/check-fetch.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { makePaginationQuery } from "../api/common/make-pagination-query.js";
import { setResponseType } from "../activitypub.js";
import type Router from "@koa/router";
@ -117,7 +117,7 @@ export default async (ctx: Router.RouterContext) => {
setResponseType(ctx);
}
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.secureMode || meta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {

View File

@ -2,7 +2,7 @@ import type Koa from "koa";
import type { User } from "@/models/entities/user.js";
import { UserIps } from "@/models/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import type { IEndpoint } from "./endpoints.js";
import authenticate, { AuthenticationError } from "./authenticate.js";
import call from "./call.js";
@ -84,7 +84,7 @@ export default (endpoint: IEndpoint, ctx: Koa.Context) =>
// Log IP
if (user) {
fetchMeta().then((meta) => {
fetchMeta(true).then((meta) => {
if (!meta.enableIpLogging) return;
const ip = ctx.ip;
const ips = userIpHistories.get(user.id);

View File

@ -10,7 +10,7 @@ import endpoints from "./endpoints.js";
import compatibility from "./compatibility.js";
import { ApiError } from "./error.js";
import { apiLogger } from "./logger.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
const accessDenied = {
message: "Access denied.",
@ -117,7 +117,7 @@ export default async (
}
// private mode
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (
meta.privateMode &&
ep.meta.requireCredentialPrivateMode &&

View File

@ -4,12 +4,11 @@ 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, toPuny } from "backend-rs";
import { genId, 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";
import config from "@/config/index.js";
import { hashPassword } from "@/misc/password.js";
export async function signup(opts: {
username: User["username"];
@ -40,7 +39,7 @@ export async function signup(opts: {
}
// Generate hash of password
hash = await hashPassword(password);
hash = hashPassword(password);
}
// Generate secret

View File

@ -1,8 +1,7 @@
import define from "@/server/api/define.js";
import { Emojis } from "@/models/index.js";
import { toPuny } from "backend-rs";
import { sqlLikeEscape, toPuny } from "backend-rs";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { ApiError } from "@/server/api/error.js";
export const meta = {

View File

@ -1,8 +1,8 @@
import define from "@/server/api/define.js";
import { Emojis } from "@/models/index.js";
import { makePaginationQuery } from "../../../common/make-pagination-query.js";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
import type { Emoji } from "@/models/entities/emoji.js";
//import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
//import { sqlLikeEscape } from "backend-rs";
import { ApiError } from "@/server/api/error.js";
export const meta = {

View File

@ -1,5 +1,5 @@
import config from "@/config/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "@/const.js";
import define from "@/server/api/define.js";
@ -466,7 +466,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async () => {
const instance = await fetchMeta(true);
const instance = await fetchMeta(false);
return {
maintainerName: instance.maintainerName,

View File

@ -1,8 +1,7 @@
import define from "@/server/api/define.js";
// import bcrypt from "bcryptjs";
import rndstr from "rndstr";
import { Users, UserProfiles } from "@/models/index.js";
import { hashPassword } from "@/misc/password.js";
import { hashPassword } from "backend-rs";
export const meta = {
tags: ["admin"],
@ -48,8 +47,7 @@ export default define(meta, paramDef, async (ps) => {
const passwd = rndstr("a-zA-Z0-9", 8);
// Generate hash of password
// const hash = bcrypt.hashSync(passwd);
const hash = await hashPassword(passwd);
const hash = hashPassword(passwd);
await UserProfiles.update(
{

View File

@ -1,6 +1,6 @@
import { Users } from "@/models/index.js";
import define from "@/server/api/define.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "backend-rs";
export const meta = {
tags: ["admin"],

View File

@ -2,7 +2,7 @@ import define from "@/server/api/define.js";
import { Brackets } from "typeorm";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
import { Channels } from "@/models/index.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "backend-rs";
export const meta = {
tags: ["channels"],

View File

@ -1,5 +1,5 @@
// import { IsNull } from 'typeorm';
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import define from "@/server/api/define.js";
export const meta = {
@ -27,7 +27,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async () => {
const meta = await fetchMeta();
const meta = await fetchMeta(true);
const motd = await Promise.all(meta.customMotd.map((x) => x));
return motd;
});

View File

@ -1,5 +1,5 @@
// import { IsNull } from 'typeorm';
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import define from "@/server/api/define.js";
export const meta = {
@ -27,7 +27,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async () => {
const meta = await fetchMeta();
const meta = await fetchMeta(true);
const icons = await Promise.all(meta.customSplashIcons.map((x) => x));
return icons;
});

View File

@ -1,4 +1,4 @@
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { DriveFiles } from "@/models/index.js";
import define from "@/server/api/define.js";
@ -35,7 +35,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async (ps, user) => {
const instance = await fetchMeta(true);
const instance = await fetchMeta(false);
// Calculate drive usage
const usage = await DriveFiles.calcDriveUsageOf(user.id);

View File

@ -2,7 +2,7 @@ import { addFile } from "@/services/drive/add-file.js";
import { DriveFiles } from "@/models/index.js";
import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js";
import { IdentifiableError } from "@/misc/identifiable-error.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { MINUTE } from "@/const.js";
import define from "@/server/api/define.js";
import { apiLogger } from "@/server/api/logger.js";
@ -96,7 +96,7 @@ export default define(
name = null;
}
const instanceMeta = await fetchMeta();
const instanceMeta = await fetchMeta(true);
try {
// Create file

View File

@ -1,7 +1,6 @@
import define from "@/server/api/define.js";
import { Instances } from "@/models/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { fetchMeta, sqlLikeEscape } from "backend-rs";
export const meta = {
tags: ["federation"],
@ -101,7 +100,7 @@ export default define(meta, paramDef, async (ps, me) => {
}
if (typeof ps.blocked === "boolean") {
const meta = await fetchMeta(true);
const meta = await fetchMeta(false);
if (ps.blocked) {
if (meta.blockedHosts.length === 0) {
return [];
@ -117,7 +116,7 @@ export default define(meta, paramDef, async (ps, me) => {
}
if (typeof ps.silenced === "boolean") {
const meta = await fetchMeta(true);
const meta = await fetchMeta(false);
if (ps.silenced) {
if (meta.silencedHosts.length === 0) {
return [];

View File

@ -1,6 +1,6 @@
import define from "@/server/api/define.js";
import { Hashtags } from "@/models/index.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "backend-rs";
export const meta = {
tags: ["hashtags"],

View File

@ -1,9 +1,8 @@
import { Brackets } from "typeorm";
import define from "@/server/api/define.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta, safeForSql } from "backend-rs";
import { Notes } from "@/models/index.js";
import type { Note } from "@/models/entities/note.js";
import { safeForSql } from "@/misc/safe-for-sql.js";
import { normalizeForSearch } from "@/misc/normalize-for-search.js";
/*
@ -67,7 +66,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async () => {
const instance = await fetchMeta(true);
const instance = await fetchMeta(false);
const hiddenTags = instance.hiddenTags.map((t) => normalizeForSearch(t));
const now = new Date(); // 5分単位で丸めた現在日時

View File

@ -9,7 +9,7 @@ import {
import config from "@/config/index.js";
import { procedures, hash } from "@/server/api/2fa.js";
import { publishMainStream } from "@/services/stream.js";
import { comparePassword } from "@/misc/password.js";
import { verifyPassword } from "backend-rs";
const rpIdHashReal = hash(Buffer.from(config.hostname, "utf-8"));
@ -40,8 +40,8 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, user) => {
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
// Compare password
const same = await comparePassword(ps.password, profile.password!);
// Compare passwords
const same = verifyPassword(ps.password, profile.password!);
if (!same) {
throw new Error("incorrect password");

View File

@ -2,9 +2,8 @@ import define from "@/server/api/define.js";
import { UserProfiles, AttestationChallenges } from "@/models/index.js";
import { promisify } from "node:util";
import * as crypto from "node:crypto";
import { genId } from "backend-rs";
import { genId, verifyPassword } from "backend-rs";
import { hash } from "@/server/api/2fa.js";
import { comparePassword } from "@/misc/password.js";
const randomBytes = promisify(crypto.randomBytes);
@ -25,8 +24,8 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, user) => {
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
// Compare password
const same = await comparePassword(ps.password, profile.password!);
// Compare passwords
const same = verifyPassword(ps.password, profile.password!);
if (!same) {
throw new Error("incorrect password");

View File

@ -3,7 +3,7 @@ import * as QRCode from "qrcode";
import config from "@/config/index.js";
import { UserProfiles } from "@/models/index.js";
import define from "@/server/api/define.js";
import { comparePassword } from "@/misc/password.js";
import { verifyPassword } from "backend-rs";
export const meta = {
requireCredential: true,
@ -22,8 +22,8 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, user) => {
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
// Compare password
const same = await comparePassword(ps.password, profile.password!);
// Compare passwords
const same = verifyPassword(ps.password, profile.password!);
if (!same) {
throw new Error("incorrect password");

View File

@ -1,4 +1,4 @@
import { comparePassword } from "@/misc/password.js";
import { verifyPassword } from "backend-rs";
import define from "@/server/api/define.js";
import { UserProfiles, UserSecurityKeys, Users } from "@/models/index.js";
import { publishMainStream } from "@/services/stream.js";
@ -21,8 +21,8 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, user) => {
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
// Compare password
const same = await comparePassword(ps.password, profile.password!);
// Compare passwords
const same = verifyPassword(ps.password, profile.password!);
if (!same) {
throw new Error("incorrect password");

View File

@ -1,7 +1,7 @@
import { publishMainStream } from "@/services/stream.js";
import define from "@/server/api/define.js";
import { Users, UserProfiles } from "@/models/index.js";
import { comparePassword } from "@/misc/password.js";
import { verifyPassword } from "backend-rs";
export const meta = {
requireCredential: true,
@ -20,8 +20,8 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, user) => {
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
// Compare password
const same = await comparePassword(ps.password, profile.password!);
// Compare passwords
const same = verifyPassword(ps.password, profile.password!);
if (!same) {
throw new Error("incorrect password");

View File

@ -1,6 +1,6 @@
import define from "@/server/api/define.js";
import { UserProfiles } from "@/models/index.js";
import { hashPassword, comparePassword } from "@/misc/password.js";
import { hashPassword, verifyPassword } from "backend-rs";
export const meta = {
requireCredential: true,
@ -20,8 +20,8 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, user) => {
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
// Compare password
const same = await comparePassword(ps.currentPassword, profile.password!);
// Compare passwords
const same = verifyPassword(ps.currentPassword, profile.password!);
if (!same) {
throw new Error("incorrect password");

View File

@ -1,7 +1,7 @@
import { UserProfiles, Users } from "@/models/index.js";
import { deleteAccount } from "@/services/delete-account.js";
import define from "@/server/api/define.js";
import { comparePassword } from "@/misc/password.js";
import { verifyPassword } from "backend-rs";
export const meta = {
requireCredential: true,
@ -24,8 +24,8 @@ export default define(meta, paramDef, async (ps, user) => {
return;
}
// Compare password
const same = await comparePassword(ps.password, profile.password!);
// Compare passwords
const same = verifyPassword(ps.password, profile.password!);
if (!same) {
throw new Error("incorrect password");

View File

@ -3,7 +3,7 @@ import { createImportPostsJob } from "@/queue/index.js";
import { ApiError } from "@/server/api/error.js";
import { DriveFiles } from "@/models/index.js";
import { DAY } from "@/const.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
export const meta = {
secure: true,
@ -45,7 +45,7 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, user) => {
const file = await DriveFiles.findOneBy({ id: ps.fileId });
const instanceMeta = await fetchMeta();
const instanceMeta = await fetchMeta(true);
if (instanceMeta.experimentalFeatures?.postImports === false)
throw new ApiError(meta.errors.importsDisabled);

View File

@ -6,7 +6,7 @@ import {
import generateUserToken from "@/server/api/common/generate-native-user-token.js";
import define from "@/server/api/define.js";
import { Users, UserProfiles } from "@/models/index.js";
import { comparePassword } from "@/misc/password.js";
import { verifyPassword } from "backend-rs";
export const meta = {
requireCredential: true,
@ -28,8 +28,8 @@ export default define(meta, paramDef, async (ps, user) => {
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
// Compare password
const same = await comparePassword(ps.password, profile.password!);
// Compare passwords
const same = verifyPassword(ps.password, profile.password!);
if (!same) {
throw new Error("incorrect password");

View File

@ -7,7 +7,7 @@ import { sendEmail } from "@/services/send-email.js";
import { ApiError } from "@/server/api/error.js";
import { validateEmailForAccount } from "@/services/validate-email-for-account.js";
import { HOUR } from "@/const.js";
import { comparePassword } from "@/misc/password.js";
import { verifyPassword } from "backend-rs";
export const meta = {
requireCredential: true,
@ -46,8 +46,8 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, user) => {
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
// Compare password
const same = await comparePassword(ps.password, profile.password!);
// Compare passwords
const same = verifyPassword(ps.password, profile.password!);
if (!same) {
throw new ApiError(meta.errors.incorrectPassword);

View File

@ -1,7 +1,7 @@
import JSON5 from "json5";
import { IsNull, MoreThan } from "typeorm";
import config from "@/config/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { Ads, Emojis, Users } from "@/models/index.js";
import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "@/const.js";
import define from "@/server/api/define.js";
@ -398,7 +398,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async (ps, me) => {
const instance = await fetchMeta(true);
const instance = await fetchMeta(false);
const emojis = await Emojis.find({
where: {

View File

@ -1,4 +1,4 @@
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { Notes } from "@/models/index.js";
import { activeUsersChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
@ -64,7 +64,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async (ps, user) => {
const m = await fetchMeta();
const m = await fetchMeta(true);
if (m.disableGlobalTimeline) {
if (user == null || !(user.isAdmin || user.isModerator)) {
throw new ApiError(meta.errors.gtlDisabled);

View File

@ -1,5 +1,5 @@
import { Brackets } from "typeorm";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { Followings, Notes } from "@/models/index.js";
import { activeUsersChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
@ -71,7 +71,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async (ps, user) => {
const m = await fetchMeta();
const m = await fetchMeta(true);
if (m.disableLocalTimeline && !user.isAdmin && !user.isModerator) {
throw new ApiError(meta.errors.stlDisabled);
}

View File

@ -1,5 +1,5 @@
import { Brackets } from "typeorm";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { Notes } from "@/models/index.js";
import { activeUsersChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
@ -74,7 +74,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async (ps, user) => {
const m = await fetchMeta();
const m = await fetchMeta(true);
if (m.disableLocalTimeline) {
if (user == null || !(user.isAdmin || user.isModerator)) {
throw new ApiError(meta.errors.ltlDisabled);

View File

@ -53,7 +53,7 @@ export default define(meta, paramDef, async (ps, user) => {
throw new ApiError(meta.errors.accessDenied);
}
await deleteNote(user, note, false, false);
await deleteNote(user, note, false);
await Notes.update(note.id, {
visibility: "specified",
visibleUserIds: [],

View File

@ -1,5 +1,5 @@
import { Brackets } from "typeorm";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { Notes } from "@/models/index.js";
import { activeUsersChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
@ -74,7 +74,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async (ps, user) => {
const m = await fetchMeta();
const m = await fetchMeta(true);
if (m.disableRecommendedTimeline) {
if (user == null || !(user.isAdmin || user.isModerator)) {
throw new ApiError(meta.errors.rtlDisabled);

View File

@ -1,6 +1,6 @@
import { Brackets } from "typeorm";
import { Notes } from "@/models/index.js";
import { safeForSql } from "@/misc/safe-for-sql.js";
import { safeForSql } from "backend-rs";
import { normalizeForSearch } from "@/misc/normalize-for-search.js";
import define from "@/server/api/define.js";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";

View File

@ -1,11 +1,11 @@
import { Notes } from "@/models/index.js";
import { Note } from "@/models/entities/note.js";
import type { Note } from "@/models/entities/note.js";
import define from "@/server/api/define.js";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "backend-rs";
import type { SelectQueryBuilder } from "typeorm";
export const meta = {

View File

@ -1,6 +1,6 @@
import { IsNull } from "typeorm";
import { Users } from "@/models/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { stringToAcct } from "backend-rs";
import type { User } from "@/models/entities/user.js";
import define from "@/server/api/define.js";
@ -31,7 +31,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async (ps, me) => {
const meta = await fetchMeta();
const meta = await fetchMeta(true);
const users = await Promise.all(
meta.pinnedUsers

View File

@ -1,5 +1,5 @@
// import { IsNull } from 'typeorm';
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import define from "@/server/api/define.js";
export const meta = {
@ -27,7 +27,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async () => {
const meta = await fetchMeta();
const meta = await fetchMeta(true);
const instances = await Promise.all(meta.recommendedInstances.map((x) => x));
return instances;
});

View File

@ -1,6 +1,6 @@
import { UserProfiles, PasswordResetRequests } from "@/models/index.js";
import define from "@/server/api/define.js";
import { hashPassword } from "@/misc/password.js";
import { hashPassword } from "backend-rs";
export const meta = {
tags: ["reset password"],
@ -32,7 +32,7 @@ export default define(meta, paramDef, async (ps, user) => {
}
// Generate hash of password
const hash = await hashPassword(ps.password);
const hash = hashPassword(ps.password);
await UserProfiles.update(req.userId, {
password: hash,

View File

@ -1,7 +1,7 @@
import * as os from "node:os";
import si from "systeminformation";
import define from "@/server/api/define.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
export const meta = {
requireCredential: false,
@ -30,7 +30,7 @@ export default define(meta, paramDef, async () => {
}
}
const instanceMeta = await fetchMeta();
const instanceMeta = await fetchMeta(true);
if (!instanceMeta.enableServerMachineStats) {
return {
machine: "Not specified",

View File

@ -1,4 +1,4 @@
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { genId } from "backend-rs";
import { SwSubscriptions } from "@/models/index.js";
import define from "@/server/api/define.js";
@ -64,7 +64,7 @@ export default define(meta, paramDef, async (ps, me) => {
publickey: ps.publickey,
});
const instance = await fetchMeta(true);
const instance = await fetchMeta(false);
// if already subscribed
if (subscription != null) {

View File

@ -4,7 +4,7 @@ import { publishAdminStream } from "@/services/stream.js";
import { AbuseUserReports, UserProfiles, Users } from "@/models/index.js";
import { genId } from "backend-rs";
import { sendEmail } from "@/services/send-email.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { getUser } from "@/server/api/common/getters.js";
import { ApiError } from "@/server/api/error.js";
import define from "@/server/api/define.js";
@ -86,7 +86,7 @@ export default define(meta, paramDef, async (ps, me) => {
],
});
const meta = await fetchMeta();
const meta = await fetchMeta(true);
for (const moderator of moderators) {
publishAdminStream(moderator.id, "newAbuseUserReport", {
id: report.id,

View File

@ -2,7 +2,7 @@ import { Brackets } from "typeorm";
import { Followings, Users } from "@/models/index.js";
import type { User } from "@/models/entities/user.js";
import define from "@/server/api/define.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "backend-rs";
export const meta = {
tags: ["users"],

View File

@ -2,7 +2,7 @@ import { Brackets } from "typeorm";
import { UserProfiles, Users } from "@/models/index.js";
import type { User } from "@/models/entities/user.js";
import define from "@/server/api/define.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "backend-rs";
export const meta = {
tags: ["users"],

Some files were not shown because too many files have changed in this diff Show More