Merge branch 'develop' into feat/antenna_limit
This commit is contained in:
commit
4c91e8e37f
341 changed files with 4405 additions and 3244 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -3,30 +3,47 @@
|
|||
🔒 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.)_
|
||||
|
||||
**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.)_
|
||||
## What did you expect to happen? <!-- Please give us a brief description of what you expected to happen. -->
|
||||
|
||||
**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.)_
|
||||
## 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. -->
|
||||
|
||||
**What browser are you using? (Client-side issues only)**
|
||||
|
||||
**What operating system are you using? (Client-side issues only)**
|
||||
## 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. -->
|
||||
|
||||
**How do you deploy Firefish on your server? (Server-side issues only)**
|
||||
- [ ] server-side
|
||||
- [ ] client-side
|
||||
- [ ] not sure
|
||||
|
||||
**What operating system are you using? (Server-side issues only)**
|
||||
<details>
|
||||
|
||||
**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.)_
|
||||
### Instance <!-- What instance of firefish are you using? -->
|
||||
|
||||
**Contribution Guidelines**
|
||||
|
||||
### What browser are you using? (client-side issues only)
|
||||
|
||||
|
||||
### What operating system are you using? (client-side issues only)
|
||||
|
||||
|
||||
### How do you deploy Firefish on your server? (server-side issues only)
|
||||
|
||||
|
||||
### 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.
|
||||
|
|
|
@ -3,18 +3,22 @@
|
|||
🔒 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.)_
|
||||
|
||||
**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.)_
|
||||
## Why should we add this feature? <!-- Please give us a brief description of why your feature is important. -->
|
||||
|
||||
**Instance** _(What instance of firefish are you using?)_
|
||||
|
||||
**Contribution Guidelines**
|
||||
## 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? -->
|
||||
|
||||
|
||||
## 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.
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<!-- 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
|
||||
|
|
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
|
@ -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
207
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.6.4/schema.json",
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
"enabled": false
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
|
@ -21,7 +21,8 @@
|
|||
"useImportType": "warn",
|
||||
"useShorthandFunctionType": "warn",
|
||||
"useTemplate": "warn",
|
||||
"noNonNullAssertion": "off"
|
||||
"noNonNullAssertion": "off",
|
||||
"useNodejsImportProtocol": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ services:
|
|||
# - web
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
NODE_OPTIONS: --max-old-space-size=3072
|
||||
volumes:
|
||||
- ./custom:/firefish/custom:ro
|
||||
- ./files:/firefish/files
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
Breaking changes are indicated by the :warning: icon.
|
||||
|
||||
## v20240413
|
||||
|
||||
- :warning: Removed `patrons` endpoint.
|
||||
|
||||
## v20240405
|
||||
|
||||
- Added `notes/history` endpoint.
|
||||
|
|
|
@ -5,9 +5,16 @@ Critical security updates are indicated by the :warning: icon.
|
|||
- Server administrators should check [notice-for-admins.md](./notice-for-admins.md) as well.
|
||||
- Third-party client/bot developers may want to check [api-change.md](./api-change.md) as well.
|
||||
|
||||
## Unreleased
|
||||
## [v20240421](https://firefish.dev/firefish/firefish/-/merge_requests/10756/commits)
|
||||
|
||||
- Fix bugs
|
||||
|
||||
## [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)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
BEGIN;
|
||||
|
||||
DELETE FROM "migrations" WHERE name IN (
|
||||
'AddDriveFileUsage1713451569342',
|
||||
'ConvertCwVarcharToText1713225866247',
|
||||
'FixChatFileConstraint1712855579316',
|
||||
'DropTimeZone1712425488543',
|
||||
'ExpandNoteEdit1711936358554',
|
||||
|
@ -22,6 +24,15 @@ DELETE FROM "migrations" WHERE name IN (
|
|||
'RemoveNativeUtilsMigration1705877093218'
|
||||
);
|
||||
|
||||
-- AddDriveFileUsage
|
||||
ALTER TABLE "drive_file" DROP COLUMN "usageHint";
|
||||
DROP TYPE "drive_file_usage_hint_enum";
|
||||
|
||||
-- convert-cw-varchar-to-text
|
||||
DROP INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f";
|
||||
ALTER TABLE "note" ALTER COLUMN "cw" TYPE character varying(512);
|
||||
CREATE INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f" ON "note" USING "pgroonga" ("cw" pgroonga_varchar_full_text_search_ops_v2);
|
||||
|
||||
-- fix-chat-file-constraint
|
||||
ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_535def119223ac05ad3fa9ef64b";
|
||||
ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_535def119223ac05ad3fa9ef64b" FOREIGN KEY ("fileId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION;
|
||||
|
|
|
@ -154,7 +154,7 @@ sudo apt install ffmpeg
|
|||
1. Build
|
||||
```sh
|
||||
pnpm install --frozen-lockfile
|
||||
NODE_ENV=production pnpm run build
|
||||
NODE_ENV=production NODE_OPTIONS='--max-old-space-size=3072' pnpm run build
|
||||
```
|
||||
1. Execute database migrations
|
||||
```sh
|
||||
|
@ -242,6 +242,7 @@ In this instruction, we use [Caddy](https://caddyserver.com/) to make the Firefi
|
|||
WorkingDirectory=/home/firefish/firefish
|
||||
Environment="NODE_ENV=production"
|
||||
Environment="npm_config_cache=/tmp"
|
||||
Environment="NODE_OPTIONS=--max-old-space-size=3072"
|
||||
# uncomment the following line if you use jemalloc (note that the path varies on different environments)
|
||||
# Environment="LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
|
||||
StandardOutput=journal
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -21,8 +21,8 @@ The number of posts stored on your database can be found at `https://yourserver.
|
|||
|
||||
- Please remove `packages/backend-rs/target` before building Firefish.
|
||||
```sh
|
||||
rm --recursive --force packages/backend-rs/target
|
||||
```
|
||||
rm --recursive --force packages/backend-rs/target
|
||||
```
|
||||
- Please do not terminate `pnpm run migrate` even if it appears to be frozen.
|
||||
|
||||
### For Docker/Podman users
|
||||
|
|
|
@ -893,9 +893,6 @@ _aboutFirefish:
|
|||
source: "الشفرة المصدرية"
|
||||
translation: "ترجم ميسكي"
|
||||
donate: "تبرع لميسكي"
|
||||
morePatrons: "نحن نقدر الدعم الذي قدمه العديد من الأشخاص الذين لم نذكرهم. شكرًا
|
||||
لكم 🥰"
|
||||
patrons: "الداعمون"
|
||||
_nsfw:
|
||||
respect: "اخف الوسائط ذات المحتوى الحساس"
|
||||
ignore: "اعرض الوسائط ذات المحتوى الحساس"
|
||||
|
|
|
@ -975,8 +975,6 @@ _aboutFirefish:
|
|||
source: "সোর্স কোড"
|
||||
translation: "Firefish অনুবাদ করুন"
|
||||
donate: "Firefish তে দান করুন"
|
||||
morePatrons: "আরও অনেকে আমাদের সাহায্য করছেন। তাদের সবাইকে ধন্যবাদ 🥰"
|
||||
patrons: "সমর্থনকারী"
|
||||
_nsfw:
|
||||
respect: "স্পর্শকাতর মিডিয়া লুকান"
|
||||
ignore: "স্পর্শকাতর মিডিয়া লুকাবেন না"
|
||||
|
|
|
@ -1586,18 +1586,12 @@ _aboutFirefish:
|
|||
translation: Tradueix Firefish
|
||||
about: Firefish és una bifurcació de Misskey feta per ThatOneCalculator, que està
|
||||
en desenvolupament des del 2022.
|
||||
morePatrons: També agraïm el suport de molts altres ajudants que no figuren aquí.
|
||||
Gràcies! 🥰
|
||||
patrons: Mecenes de Firefish
|
||||
patronsList: Llistats cronològicament, no per la quantitat donada. Fes una donació
|
||||
amb l'enllaç de dalt per veure el teu nom aquí!
|
||||
donateTitle: T'agrada Firefish?
|
||||
pleaseDonateToFirefish: Penseu en fer una donació a Firefish per donar suport al
|
||||
seu desenvolupament.
|
||||
pleaseDonateToHost: Penseu també en fer una donació a la vostre instància, {host},
|
||||
per ajudar-lo a suportar els costos de funcionament.
|
||||
donateHost: Fes una donació a {host}
|
||||
sponsors: Patrocinadors de Calckey
|
||||
misskeyContributors: Col·laboradors de Misskey
|
||||
unknown: Desconegut
|
||||
pageLikesCount: Nombre de pàgines amb M'agrada
|
||||
|
@ -2084,9 +2078,9 @@ _experiments:
|
|||
release: Publicà
|
||||
title: Experiments
|
||||
enablePostImports: Activar l'importació de publicacions
|
||||
postImportsCaption: Permet els usuaris importar publicacions desde comptes a Firefish,
|
||||
postImportsCaption: Permet als usuaris importar publicacions des de comptes de Firefish,
|
||||
Misskey, Mastodon, Akkoma i Pleroma. Pot fer que el servidor vagi més lent durant
|
||||
la càrrega si tens un coll d'ampolla a la cua.
|
||||
la importació si la teva cua de feina és saturada.
|
||||
noGraze: Si us plau, desactiva l'extensió del navegador "Graze for Mastodon", ja que
|
||||
interfereix amb Firefish.
|
||||
accessibility: Accessibilitat
|
||||
|
|
|
@ -1092,17 +1092,11 @@ _aboutFirefish:
|
|||
source: "Quellcode"
|
||||
translation: "Firefish übersetzen"
|
||||
donate: "An Firefish spenden"
|
||||
morePatrons: "Wir schätzen ebenso die Unterstützung vieler anderer hier nicht gelisteter
|
||||
Personen sehr. Danke! 🥰"
|
||||
patrons: "UnterstützerInnen"
|
||||
patronsList: Auflistung chonologisch, nicht nach Spenden-Größe. Spende über den
|
||||
Link oben, um hier aufgeführt zu werden!
|
||||
donateTitle: Gefällt dir Firefish?
|
||||
pleaseDonateToFirefish: Bitte erwäge eine Spende an Firefish, um dessen Entwicklung
|
||||
zu unterstützen.
|
||||
pleaseDonateToHost: Bitte erwäge auch, an deinen Heimatserver {host} zu spenden,
|
||||
um bei der Deckung der Betriebskosten zu helfen.
|
||||
sponsors: Firefish-Sponsoren
|
||||
donateHost: Spende an {host}
|
||||
misskeyContributors: Misskey-Mitwirkende
|
||||
_nsfw:
|
||||
|
|
|
@ -1337,12 +1337,6 @@ _aboutFirefish:
|
|||
pleaseDonateToHost: "Please also consider donating to your home server, {host},
|
||||
to help support its operation costs."
|
||||
donateHost: "Donate to {host}"
|
||||
morePatrons: "We also appreciate the support of many other helpers not listed here.
|
||||
Thank you! 🥰"
|
||||
sponsors: "Firefish sponsors"
|
||||
patrons: "Firefish patrons"
|
||||
patronsList: "Listed chronologically, not by donation size. Donate with the link
|
||||
above to get your name on here!"
|
||||
_nsfw:
|
||||
respect: "Hide NSFW media"
|
||||
ignore: "Don't hide NSFW media"
|
||||
|
|
|
@ -1073,17 +1073,11 @@ _aboutFirefish:
|
|||
source: "Código fuente"
|
||||
translation: "Traducir Firefish"
|
||||
donate: "Donar a Firefish"
|
||||
morePatrons: "También apreciamos el apoyo de muchos más que no están enlistados
|
||||
aquí. ¡Gracias! 🥰"
|
||||
patrons: "Mecenas de Firefish"
|
||||
pleaseDonateToFirefish: Por favor considera donar a Firefish para apollar su desarrollo.
|
||||
donateHost: Dona a {host}
|
||||
patronsList: Listados cronológicamente no por monto de la donación. ¡Dona con el
|
||||
vínculo de arriba para que tu nombre aparezca aquí!
|
||||
donateTitle: ¿Te gusta Firefish?
|
||||
pleaseDonateToHost: También considera donar a tu propio servidor , {host}, para
|
||||
ayudar con los costos de operación.
|
||||
sponsors: Patrocinadores de Firefish
|
||||
misskeyContributors: Contribuidores de Misskey
|
||||
_nsfw:
|
||||
respect: "Ocultar medios NSFW"
|
||||
|
|
|
@ -996,18 +996,12 @@ _aboutFirefish:
|
|||
source: "Code source"
|
||||
translation: "Traduire Firefish"
|
||||
donate: "Soutenir Firefish"
|
||||
morePatrons: "Nous apprécions vraiment le soutien de nombreuses autres personnes
|
||||
non mentionnées ici. Merci à toutes et à tous ! 🥰"
|
||||
patrons: "Contributeurs"
|
||||
pleaseDonateToFirefish: Merci de considérer de faire un don pour soutenir le développement
|
||||
de Firefish.
|
||||
sponsors: Sponsors Firefish
|
||||
donateTitle: Firefish vous plaît ?
|
||||
pleaseDonateToHost: Également, veuillez envisager de faire un don à votre serveur
|
||||
d'accueil, {host}, pour contribuer à couvrir ses frais de fonctionnement.
|
||||
donateHost: Faire un don à {host}
|
||||
patronsList: Listé chronologiquement, pas par taille de donation. Faite un don avec
|
||||
le lien ci-dessus pour avoir votre nom affiché ici !
|
||||
misskeyContributors: Contributeurs Misskey
|
||||
_nsfw:
|
||||
respect: "Cacher les médias marqués comme contenu sensible (NSFW)"
|
||||
|
@ -2324,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
|
||||
|
|
|
@ -985,12 +985,6 @@ _aboutFirefish:
|
|||
source: "Sumber kode"
|
||||
translation: "Terjemahkan Firefish"
|
||||
donate: "Donasi ke Firefish"
|
||||
morePatrons: "Kami sangat mengapresiasi dukungan dari banyak penolong lain yang
|
||||
tidak tercantum disini. Terima kasih! 🥰"
|
||||
patrons: "Pendukung"
|
||||
patronsList: Diurutkan secara kronologis, bukan berdasarkan jumlah donasi. Berdonasilah
|
||||
dengan tautan di atas supaya nama kamu ada di sini!
|
||||
sponsors: Sponsor Firefish
|
||||
donateTitle: Suka Firefish?
|
||||
pleaseDonateToFirefish: Silakan pertimbangkan berdonasi ke Firefish untuk mendukung
|
||||
pengembangannya.
|
||||
|
|
|
@ -934,18 +934,12 @@ _aboutFirefish:
|
|||
source: "Codice sorgente"
|
||||
translation: "Traduzione di Firefish"
|
||||
donate: "Sostieni Firefish"
|
||||
morePatrons: "Apprezziamo sinceramente l'aiuto di tante altre persone non elencate
|
||||
qui. Grazie mille! 🥰"
|
||||
patrons: "Sostenitori"
|
||||
sponsors: Gli sponsor di Firefish
|
||||
misskeyContributors: Contributori di Misskey
|
||||
donateTitle: Ti piace Firefish?
|
||||
pleaseDonateToFirefish: Con una donazione puoi supportare lo sviluppo di Firefish.
|
||||
pleaseDonateToHost: Considera anche una donazione al server che ti ospita, {host},
|
||||
per contribuire ai costi che sostiene.
|
||||
donateHost: Dona a {host}
|
||||
patronsList: Elencati in ordine cronologico, non per importo. Dona con il link sopra
|
||||
per apparire in questa lista!
|
||||
_nsfw:
|
||||
respect: "Nascondi i media sensibli (NSFW)"
|
||||
ignore: "Mostra i media sensibili (NSFW)"
|
||||
|
|
|
@ -1109,14 +1109,10 @@ _aboutFirefish:
|
|||
source: "ソースコード"
|
||||
translation: "Firefishを翻訳"
|
||||
donate: "Firefishに寄付"
|
||||
morePatrons: "他にも多くの方が支援してくれています。ありがとうございます! 🥰"
|
||||
patrons: "支援者"
|
||||
patronsList: 寄付額ではなく時系列順に並んでいます。上記のリンクから寄付を行ってここにあなたのIDを載せましょう!
|
||||
pleaseDonateToFirefish: Firefish開発への寄付をご検討ください。
|
||||
pleaseDonateToHost: また、このサーバー {host} の運営者への寄付もご検討ください。
|
||||
donateHost: '{host} に寄付する'
|
||||
donateTitle: Firefishを気に入りましたか?
|
||||
sponsors: Firefish の支援者
|
||||
_nsfw:
|
||||
respect: "閲覧注意のメディアは隠す"
|
||||
ignore: "閲覧注意のメディアを隠さない"
|
||||
|
|
|
@ -874,8 +874,6 @@ _aboutFirefish:
|
|||
source: "ソースコード"
|
||||
translation: "Firefishを翻訳"
|
||||
donate: "Firefishに寄付"
|
||||
morePatrons: "他にもぎょうさんの人からサポートしてもろてんねん。ほんまおおきに🥰"
|
||||
patrons: "支援者"
|
||||
misskeyContributors: フォーク元のMisskeyを作らはった人ら
|
||||
_mfm:
|
||||
cheatSheet: "MFMチートシート"
|
||||
|
|
|
@ -992,10 +992,6 @@ _aboutFirefish:
|
|||
source: "소스 코드"
|
||||
translation: "Firefish를 번역하기"
|
||||
donate: "Firefish에 기부하기"
|
||||
morePatrons: "이 외에도 다른 많은 분들이 도움을 주시고 계십니다. 감사합니다🥰"
|
||||
patrons: "후원자"
|
||||
patronsList: 기부 금액이 아닌 시간 순서로 정렬합니다. 위 링크를 통해 후원하여 당신의 이름을 새겨 보세요!
|
||||
sponsors: Firefish 스폰서
|
||||
pleaseDonateToHost: 또한, 이 서버 {host} 의 운영자에게 기부하는 것도 검토하여 주십시오.
|
||||
pleaseDonateToFirefish: Firefish의 개발에 후원하는 것을 검토하여 주십시오.
|
||||
donateHost: '{host} 에게 기부하기'
|
||||
|
|
|
@ -987,8 +987,6 @@ _aboutFirefish:
|
|||
pleaseDonateToFirefish: Du kan vurdere å donere en slant til Firefish for å støtte
|
||||
videre utvikling og feilretting.
|
||||
donateHost: Donér til {host}
|
||||
morePatrons: Vi er også takknemlige for bidragene fra mange andre som ikke er listet
|
||||
her. Takk til dere alle! 🥰
|
||||
contributors: Hovedutviklere
|
||||
source: Kildekode
|
||||
allContributors: Alle bidragsytere
|
||||
|
@ -996,10 +994,6 @@ _aboutFirefish:
|
|||
pleaseDonateToHost: Du kan også vurdere å donere til hjemme-tjeneren din, {host},
|
||||
for å hjelpe dem med driftskostnadene for tjenesten.
|
||||
about: Firefish ble opprettet av ThatOneCalculator i 2022, basert på Misskey.
|
||||
sponsors: Firefishs sponsorer
|
||||
patrons: Firefishs patroner
|
||||
patronsList: Listen er kronologisk, ikke etter donert beløp. Doner med lenken over
|
||||
for å få navnet ditt her!
|
||||
isBot: Denne kontoen er en bot
|
||||
_nsfw:
|
||||
respect: Skjul NSFW-merket media
|
||||
|
|
|
@ -990,9 +990,6 @@ _aboutFirefish:
|
|||
source: "Kod źródłowy"
|
||||
translation: "Tłumacz Firefish"
|
||||
donate: "Przekaż darowiznę na Firefish"
|
||||
morePatrons: "Naprawdę doceniam wsparcie ze strony wielu niewymienionych tu osób.
|
||||
Dziękuję! 🥰"
|
||||
patrons: "Wspierający"
|
||||
_nsfw:
|
||||
respect: "Ukrywaj media NSFW"
|
||||
ignore: "Nie ukrywaj mediów NSFW"
|
||||
|
|
|
@ -986,12 +986,6 @@ _aboutFirefish:
|
|||
source: "Исходный код"
|
||||
translation: "Перевод Firefish"
|
||||
donate: "Пожертвование на Firefish"
|
||||
morePatrons: "Большое спасибо и многим другим, кто принял участие в этом проекте!
|
||||
🥰"
|
||||
patrons: "Материальная поддержка"
|
||||
patronsList: Перечислены в хронологическом порядке, а не по размеру пожертвования.
|
||||
Сделайте взнос по ссылке выше, чтобы ваше имя было здесь!
|
||||
sponsors: Спонсоры Firefish
|
||||
donateTitle: Понравился Firefish?
|
||||
pleaseDonateToFirefish: Пожалуйста, поддержите разработку Firefish.
|
||||
pleaseDonateToHost: Также не забудьте поддержать ваш домашний сервер {host}, чтобы
|
||||
|
|
|
@ -1036,9 +1036,6 @@ _aboutFirefish:
|
|||
source: "Zdrojový kód"
|
||||
translation: "Preložiť Firefish"
|
||||
donate: "Podporiť Firefish"
|
||||
morePatrons: "Takisto oceňujeme podporu mnoých ďalších, ktorí tu nie sú uvedení.
|
||||
Ďakujeme! 🥰"
|
||||
patrons: "Prispievatelia"
|
||||
_nsfw:
|
||||
respect: "Skryť NSFW médiá"
|
||||
ignore: "Neskrývať NSFW médiá"
|
||||
|
|
|
@ -1022,9 +1022,6 @@ _aboutFirefish:
|
|||
source: "ซอร์สโค้ด"
|
||||
translation: "รับแปลภาษา Firefish"
|
||||
donate: "บริจาคให้กับ Firefish"
|
||||
morePatrons: "เราขอขอบคุณสำหรับความช่วยเหลือจากผู้ช่วยอื่นๆ ที่ไม่ได้ระบุไว้ที่นี่นะ
|
||||
ขอขอบคุณ! 🥰"
|
||||
patrons: "สมาชิกพันธมิตร"
|
||||
_nsfw:
|
||||
respect: "ซ่อนสื่อ NSFW"
|
||||
ignore: "อย่าซ่อนสื่อ NSFW"
|
||||
|
|
|
@ -1910,14 +1910,9 @@ _preferencesBackups:
|
|||
updatedAt: 'Güncelleme tarihi: {date} {time}'
|
||||
cannotLoad: Yüklenemedi
|
||||
_aboutFirefish:
|
||||
patronsList: Bağış büyüklüğüne göre değil, kronolojik olarak listelenmiştir. Adınızı
|
||||
buraya almak için yukarıdaki bağlantıyla bağış yapın!
|
||||
about: Firefish, 2022'den beri geliştirilmekte olan ThatOneCalculator tarafından
|
||||
yapılan bir Misskey çatalıdır.
|
||||
allContributors: Tüm katkıda bulunanlar
|
||||
patrons: Firefish patronları
|
||||
morePatrons: Burada listelenmeyen diğer birçok yardımcının desteğini de takdir ediyoruz.
|
||||
Teşekkür ederim! 🥰
|
||||
donate: Firefish'e bağışta bulunun
|
||||
contributors: Ana katkıda bulunanlar
|
||||
source: Kaynak Kodu
|
||||
|
@ -1928,7 +1923,6 @@ _aboutFirefish:
|
|||
pleaseDonateToHost: İşletme maliyetlerini desteklemek için lütfen ev sunucunuz {host}'a
|
||||
bağış yapmayı da düşünün.
|
||||
donateHost: '{ev sahibi} için bağış yapın'
|
||||
sponsors: Firefish sponsorları
|
||||
misskeyContributors: Misskey'e katkıda bulunanlar
|
||||
_weekday:
|
||||
saturday: Cumartesi
|
||||
|
|
|
@ -825,17 +825,11 @@ _aboutFirefish:
|
|||
source: "Вихідний код"
|
||||
translation: "Перекладати Firefish"
|
||||
donate: "Пожертвувати Firefish"
|
||||
morePatrons: "Ми дуже цінуємо підтримку багатьох інших помічників, не перелічених
|
||||
тут. Дякуємо! 🥰"
|
||||
patrons: "Підтримали"
|
||||
patronsList: Перераховані в хронологічному порядку, а не за розміром пожертви. Зробіть
|
||||
внесок за посиланням вище, щоб ваше ім'я було тут!
|
||||
donateTitle: Сподобався Firefish?
|
||||
pleaseDonateToFirefish: Будь ласка, підтримайте розробку Firefish.
|
||||
pleaseDonateToHost: Також не забудьте підтримати ваш домашній сервер {host}, щоб
|
||||
допомогти з його операційними витратами.
|
||||
donateHost: Зробити внесок на рахунок {host}
|
||||
sponsors: Спонсори Firefish
|
||||
misskeyContributors: Контрибутори Misskey
|
||||
_nsfw:
|
||||
respect: "Приховувати NSFW медіа"
|
||||
|
|
|
@ -1050,15 +1050,10 @@ _aboutFirefish:
|
|||
source: "Mã nguồn"
|
||||
translation: "Dịch Firefish"
|
||||
donate: "Ủng hộ Firefish"
|
||||
morePatrons: "Chúng tôi cũng trân trọng sự hỗ trợ của nhiều người đóng góp khác
|
||||
không được liệt kê ở đây. Cảm ơn! 🥰"
|
||||
patrons: "Người ủng hộ"
|
||||
patronsList: Liệt kê theo thứ tự, không theo số tiền ủng hộ. Hãy để tên bạn ở đây!
|
||||
donateTitle: Thích Firefish?
|
||||
pleaseDonateToFirefish: Hãy cân nhắc ủng hộ Firefish phát triển.
|
||||
donateHost: Ủng hộ {host}
|
||||
pleaseDonateToHost: Cũng như ủng hộ chi phí vận hành máy chủ {host} của bạn.
|
||||
sponsors: Nhà tài trợ Firefish
|
||||
misskeyContributors: Người đóng góp Misskey
|
||||
_nsfw:
|
||||
respect: "Ẩn nội dung NSFW"
|
||||
|
|
|
@ -996,10 +996,6 @@ _aboutFirefish:
|
|||
source: "源代码"
|
||||
translation: "翻译 Firefish"
|
||||
donate: "赞助 Firefish"
|
||||
morePatrons: "还有很多其它的人也在支持我们,非常感谢🥰"
|
||||
patrons: "Firefish 赞助者"
|
||||
patronsList: 按时间顺序而不是捐赠金额排列。通过上面的链接捐款,让您的名字出现在这里!
|
||||
sponsors: Firefish 赞助者们
|
||||
donateTitle: 喜欢 Firefish 吗?
|
||||
pleaseDonateToFirefish: 请考虑赞助 Firefish 以支持其开发。
|
||||
pleaseDonateToHost: 也请考虑赞助您的主服务器 {host},以帮助支持其运营成本。
|
||||
|
|
|
@ -991,10 +991,6 @@ _aboutFirefish:
|
|||
source: "原始碼"
|
||||
translation: "翻譯Firefish"
|
||||
donate: "贊助Firefish"
|
||||
morePatrons: "還有許許多多幫助我們的其他人,非常感謝你們。 🥰"
|
||||
patrons: "贊助者"
|
||||
patronsList: 按時間順序列出,而不是按贊助規模列出。使用上面的連結贊助,在這裡獲得顯示您名字的機會!
|
||||
sponsors: Firefish 贊助者們
|
||||
donateTitle: 覺得 Firefish 棒嗎?
|
||||
pleaseDonateToFirefish: 請考慮向 Firefish 贊助以支持其發展。
|
||||
pleaseDonateToHost: 還請考慮捐贈給您在使用的伺服器 {host},以支援龐大的運營成本。
|
||||
|
|
14
package.json
14
package.json
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"name": "firefish",
|
||||
"version": "20240405",
|
||||
"version": "20240421",
|
||||
"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",
|
||||
|
@ -26,7 +26,9 @@
|
|||
"debug": "pnpm run build:debug && pnpm run start",
|
||||
"build:debug": "pnpm run clean && pnpm node ./scripts/dev-build.mjs && pnpm run gulp",
|
||||
"mocha": "pnpm --filter backend run mocha",
|
||||
"test": "pnpm run mocha",
|
||||
"test": "pnpm run test:ts && pnpm run test:rs",
|
||||
"test:ts": "pnpm run mocha",
|
||||
"test:rs": "cargo test",
|
||||
"format": "pnpm run format:ts; pnpm run format:rs",
|
||||
"format:ts": "pnpm -r --parallel run format",
|
||||
"format:rs": "cargo fmt --all --",
|
||||
|
@ -36,11 +38,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 +52,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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -7,9 +7,9 @@ SRC += $(call recursive_wildcard, src, *)
|
|||
.PHONY: regenerate-entities
|
||||
regenerate-entities:
|
||||
sea-orm-cli generate entity \
|
||||
--output-dir='src/model/entity' \
|
||||
--database-url='postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@localhost:25432/$(POSTGRES_DB)' \
|
||||
--date-time-crate='chrono' \
|
||||
--output-dir='src/model/entity' \
|
||||
--database-url='postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@localhost:25432/$(POSTGRES_DB)' \
|
||||
--date-time-crate='chrono' \
|
||||
--model-extra-attributes='NAPI_EXTRA_ATTR_PLACEHOLDER' && \
|
||||
for file in src/model/entity/*; do \
|
||||
base=$$(basename -- "$${file}"); \
|
||||
|
@ -17,7 +17,7 @@ regenerate-entities:
|
|||
attribute=$$(printf 'cfg_attr(feature = "napi", napi_derive::napi(object, js_name = "%s", use_nullable = true))' "$${jsname}"); \
|
||||
sed -i "s/NAPI_EXTRA_ATTR_PLACEHOLDER/$${attribute}/" "$${file}"; \
|
||||
done
|
||||
sed -i 's/#\[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)\]/#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]\n#[cfg_attr(not(feature = "napi"), derive(Clone))]\n#[cfg_attr(feature = "napi", napi_derive::napi)]/' \
|
||||
sed -i 's/#\[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)\]/#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]\n#[cfg_attr(not(feature = "napi"), derive(Clone))]\n#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]/' \
|
||||
src/model/entity/sea_orm_active_enums.rs
|
||||
cargo fmt --all --
|
||||
|
||||
|
|
189
packages/backend-rs/index.d.ts
vendored
189
packages/backend-rs/index.d.ts
vendored
|
@ -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
|
||||
|
@ -118,7 +128,8 @@ export interface Acct {
|
|||
}
|
||||
export function stringToAcct(acct: string): Acct
|
||||
export function acctToString(acct: Acct): string
|
||||
export interface NoteLike {
|
||||
/** TODO: handle name collisions better */
|
||||
export interface NoteLikeForCheckWordMute {
|
||||
fileIds: Array<string>
|
||||
userId: string | null
|
||||
text: string | null
|
||||
|
@ -126,15 +137,52 @@ export interface NoteLike {
|
|||
renoteId: string | null
|
||||
replyId: string | null
|
||||
}
|
||||
export function checkWordMute(note: NoteLike, mutedWordLists: Array<Array<string>>, mutedPatterns: Array<string>): Promise<boolean>
|
||||
export function checkWordMute(note: NoteLikeForCheckWordMute, mutedWordLists: Array<Array<string>>, mutedPatterns: Array<string>): Promise<boolean>
|
||||
export function getFullApAccount(username: string, host?: string | undefined | null): string
|
||||
export function isSelfHost(host?: string | undefined | null): boolean
|
||||
export function isSameOrigin(uri: string): boolean
|
||||
export function extractHost(uri: string): string
|
||||
export function toPuny(host: string): string
|
||||
export function isUnicodeEmoji(s: string): boolean
|
||||
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
|
||||
/** TODO: handle name collisions better */
|
||||
export interface NoteLikeForGetNoteSummary {
|
||||
fileIds: Array<string>
|
||||
text: string | null
|
||||
cw: string | null
|
||||
hasPoll: boolean
|
||||
}
|
||||
export function getNoteSummary(note: NoteLikeForGetNoteSummary): 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
|
||||
|
@ -300,6 +348,7 @@ export interface DriveFile {
|
|||
webpublicType: string | null
|
||||
requestHeaders: Json | null
|
||||
requestIp: string | null
|
||||
usageHint: DriveFileUsageHintEnum | null
|
||||
}
|
||||
export interface DriveFolder {
|
||||
id: string
|
||||
|
@ -725,81 +774,85 @@ export interface ReplyMuting {
|
|||
muteeId: string
|
||||
muterId: string
|
||||
}
|
||||
export const enum AntennaSrcEnum {
|
||||
All = 0,
|
||||
Group = 1,
|
||||
Home = 2,
|
||||
Instances = 3,
|
||||
List = 4,
|
||||
Users = 5
|
||||
export enum AntennaSrcEnum {
|
||||
All = 'all',
|
||||
Group = 'group',
|
||||
Home = 'home',
|
||||
Instances = 'instances',
|
||||
List = 'list',
|
||||
Users = 'users'
|
||||
}
|
||||
export const enum MutedNoteReasonEnum {
|
||||
Manual = 0,
|
||||
Other = 1,
|
||||
Spam = 2,
|
||||
Word = 3
|
||||
export enum DriveFileUsageHintEnum {
|
||||
UserAvatar = 'userAvatar',
|
||||
UserBanner = 'userBanner'
|
||||
}
|
||||
export const enum NoteVisibilityEnum {
|
||||
Followers = 0,
|
||||
Hidden = 1,
|
||||
Home = 2,
|
||||
Public = 3,
|
||||
Specified = 4
|
||||
export enum MutedNoteReasonEnum {
|
||||
Manual = 'manual',
|
||||
Other = 'other',
|
||||
Spam = 'spam',
|
||||
Word = 'word'
|
||||
}
|
||||
export const enum NotificationTypeEnum {
|
||||
App = 0,
|
||||
Follow = 1,
|
||||
FollowRequestAccepted = 2,
|
||||
GroupInvited = 3,
|
||||
Mention = 4,
|
||||
PollEnded = 5,
|
||||
PollVote = 6,
|
||||
Quote = 7,
|
||||
Reaction = 8,
|
||||
ReceiveFollowRequest = 9,
|
||||
Renote = 10,
|
||||
Reply = 11
|
||||
export enum NoteVisibilityEnum {
|
||||
Followers = 'followers',
|
||||
Hidden = 'hidden',
|
||||
Home = 'home',
|
||||
Public = 'public',
|
||||
Specified = 'specified'
|
||||
}
|
||||
export const enum PageVisibilityEnum {
|
||||
Followers = 0,
|
||||
Public = 1,
|
||||
Specified = 2
|
||||
export enum NotificationTypeEnum {
|
||||
App = 'app',
|
||||
Follow = 'follow',
|
||||
FollowRequestAccepted = 'followRequestAccepted',
|
||||
GroupInvited = 'groupInvited',
|
||||
Mention = 'mention',
|
||||
PollEnded = 'pollEnded',
|
||||
PollVote = 'pollVote',
|
||||
Quote = 'quote',
|
||||
Reaction = 'reaction',
|
||||
ReceiveFollowRequest = 'receiveFollowRequest',
|
||||
Renote = 'renote',
|
||||
Reply = 'reply'
|
||||
}
|
||||
export const enum PollNotevisibilityEnum {
|
||||
Followers = 0,
|
||||
Home = 1,
|
||||
Public = 2,
|
||||
Specified = 3
|
||||
export enum PageVisibilityEnum {
|
||||
Followers = 'followers',
|
||||
Public = 'public',
|
||||
Specified = 'specified'
|
||||
}
|
||||
export const enum RelayStatusEnum {
|
||||
Accepted = 0,
|
||||
Rejected = 1,
|
||||
Requesting = 2
|
||||
export enum PollNotevisibilityEnum {
|
||||
Followers = 'followers',
|
||||
Home = 'home',
|
||||
Public = 'public',
|
||||
Specified = 'specified'
|
||||
}
|
||||
export const enum UserEmojimodpermEnum {
|
||||
Add = 0,
|
||||
Full = 1,
|
||||
Mod = 2,
|
||||
Unauthorized = 3
|
||||
export enum RelayStatusEnum {
|
||||
Accepted = 'accepted',
|
||||
Rejected = 'rejected',
|
||||
Requesting = 'requesting'
|
||||
}
|
||||
export const enum UserProfileFfvisibilityEnum {
|
||||
Followers = 0,
|
||||
Private = 1,
|
||||
Public = 2
|
||||
export enum UserEmojimodpermEnum {
|
||||
Add = 'add',
|
||||
Full = 'full',
|
||||
Mod = 'mod',
|
||||
Unauthorized = 'unauthorized'
|
||||
}
|
||||
export const enum UserProfileMutingnotificationtypesEnum {
|
||||
App = 0,
|
||||
Follow = 1,
|
||||
FollowRequestAccepted = 2,
|
||||
GroupInvited = 3,
|
||||
Mention = 4,
|
||||
PollEnded = 5,
|
||||
PollVote = 6,
|
||||
Quote = 7,
|
||||
Reaction = 8,
|
||||
ReceiveFollowRequest = 9,
|
||||
Renote = 10,
|
||||
Reply = 11
|
||||
export enum UserProfileFfvisibilityEnum {
|
||||
Followers = 'followers',
|
||||
Private = 'private',
|
||||
Public = 'public'
|
||||
}
|
||||
export enum UserProfileMutingnotificationtypesEnum {
|
||||
App = 'app',
|
||||
Follow = 'follow',
|
||||
FollowRequestAccepted = 'followRequestAccepted',
|
||||
GroupInvited = 'groupInvited',
|
||||
Mention = 'mention',
|
||||
PollEnded = 'pollEnded',
|
||||
PollVote = 'pollVote',
|
||||
Quote = 'quote',
|
||||
Reaction = 'reaction',
|
||||
ReceiveFollowRequest = 'receiveFollowRequest',
|
||||
Renote = 'renote',
|
||||
Reply = 'reply'
|
||||
}
|
||||
export interface Signin {
|
||||
id: string
|
||||
|
|
|
@ -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, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, AntennaSrcEnum, DriveFileUsageHintEnum, 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,10 +322,24 @@ 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.getNoteSummary = getNoteSummary
|
||||
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.DriveFileUsageHintEnum = DriveFileUsageHintEnum
|
||||
module.exports.MutedNoteReasonEnum = MutedNoteReasonEnum
|
||||
module.exports.NoteVisibilityEnum = NoteVisibilityEnum
|
||||
module.exports.NotificationTypeEnum = NotificationTypeEnum
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
},
|
||||
"scripts": {
|
||||
"artifacts": "napi artifacts",
|
||||
"build": "napi build --features napi --platform --release ./built/",
|
||||
"build:debug": "napi build --features napi --platform ./built/",
|
||||
"build": "napi build --features napi --no-const-enum --platform --release ./built/",
|
||||
"build:debug": "napi build --features napi --no-const-enum --platform ./built/",
|
||||
"prepublishOnly": "napi prepublish -t npm",
|
||||
"test": "pnpm run cargo:test && pnpm run build:debug && ava",
|
||||
"universal": "napi universal",
|
||||
|
|
27
packages/backend-rs/src/config/environment.rs
Normal file
27
packages/backend-rs/src/config/environment.rs
Normal 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(),
|
||||
}
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
pub mod environment;
|
||||
pub mod server;
|
||||
|
|
|
@ -4,7 +4,8 @@ use once_cell::sync::Lazy;
|
|||
use regex::Regex;
|
||||
use sea_orm::{prelude::*, QuerySelect};
|
||||
|
||||
#[crate::export(object)]
|
||||
/// TODO: handle name collisions better
|
||||
#[crate::export(object, js_name = "NoteLikeForCheckWordMute")]
|
||||
pub struct NoteLike {
|
||||
pub file_ids: Vec<String>,
|
||||
pub user_id: Option<String>,
|
||||
|
|
31
packages/backend-rs/src/misc/emoji.rs
Normal file
31
packages/backend-rs/src/misc/emoji.rs
Normal 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:"));
|
||||
}
|
||||
}
|
36
packages/backend-rs/src/misc/escape_sql.rs
Normal file
36
packages/backend-rs/src/misc/escape_sql.rs
Normal 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%"));
|
||||
}
|
||||
}
|
46
packages/backend-rs/src/misc/format_milliseconds.rs
Normal file
46
packages/backend-rs/src/misc/format_milliseconds.rs
Normal 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)"
|
||||
);
|
||||
}
|
||||
}
|
90
packages/backend-rs/src/misc/get_note_summary.rs
Normal file
90
packages/backend-rs/src/misc/get_note_summary.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
/// TODO: handle name collisions better
|
||||
#[crate::export(object, js_name = "NoteLikeForGetNoteSummary")]
|
||||
pub struct NoteLike {
|
||||
pub file_ids: Vec<String>,
|
||||
pub text: Option<String>,
|
||||
pub cw: Option<String>,
|
||||
pub has_poll: bool,
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn get_note_summary(note: NoteLike) -> String {
|
||||
let mut buf: Vec<String> = vec![];
|
||||
|
||||
if let Some(cw) = note.cw {
|
||||
buf.push(cw)
|
||||
} else if let Some(text) = note.text {
|
||||
buf.push(text)
|
||||
}
|
||||
|
||||
match note.file_ids.len() {
|
||||
0 => (),
|
||||
1 => buf.push("📎".to_string()),
|
||||
n => buf.push(format!("📎 ({})", n)),
|
||||
};
|
||||
|
||||
if note.has_poll {
|
||||
buf.push("📊".to_string())
|
||||
}
|
||||
|
||||
buf.join(" ")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::{get_note_summary, NoteLike};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_note_summary() {
|
||||
let note = NoteLike {
|
||||
file_ids: vec![],
|
||||
text: Some("Hello world!".to_string()),
|
||||
cw: None,
|
||||
has_poll: false,
|
||||
};
|
||||
assert_eq!(get_note_summary(note), "Hello world!");
|
||||
|
||||
let note_with_cw = NoteLike {
|
||||
file_ids: vec![],
|
||||
text: Some("Hello world!".to_string()),
|
||||
cw: Some("Content warning".to_string()),
|
||||
has_poll: false,
|
||||
};
|
||||
assert_eq!(get_note_summary(note_with_cw), "Content warning");
|
||||
|
||||
let note_with_file_and_cw = NoteLike {
|
||||
file_ids: vec!["9s7fmcqogiq4igin".to_string()],
|
||||
text: None,
|
||||
cw: Some("Selfie, no ec".to_string()),
|
||||
has_poll: false,
|
||||
};
|
||||
assert_eq!(get_note_summary(note_with_file_and_cw), "Selfie, no ec 📎");
|
||||
|
||||
let note_with_files_only = NoteLike {
|
||||
file_ids: vec![
|
||||
"9s7fmcqogiq4igin".to_string(),
|
||||
"9s7qrld5u14cey98".to_string(),
|
||||
"9s7gebs5zgts4kca".to_string(),
|
||||
"9s5z3e4vefqd29ee".to_string(),
|
||||
],
|
||||
text: None,
|
||||
cw: None,
|
||||
has_poll: false,
|
||||
};
|
||||
assert_eq!(get_note_summary(note_with_files_only), "📎 (4)");
|
||||
|
||||
let note_all = NoteLike {
|
||||
file_ids: vec![
|
||||
"9s7fmcqogiq4igin".to_string(),
|
||||
"9s7qrld5u14cey98".to_string(),
|
||||
"9s7gebs5zgts4kca".to_string(),
|
||||
"9s5z3e4vefqd29ee".to_string(),
|
||||
],
|
||||
text: Some("Hello world!".to_string()),
|
||||
cw: Some("Content warning".to_string()),
|
||||
has_poll: true,
|
||||
};
|
||||
assert_eq!(get_note_summary(note_all), "Content warning 📎 (4) 📊");
|
||||
}
|
||||
}
|
83
packages/backend-rs/src/misc/meta.rs
Normal file
83
packages/backend-rs/src/misc/meta.rs
Normal 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,
|
||||
}
|
||||
}
|
|
@ -1,5 +1,12 @@
|
|||
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 get_note_summary;
|
||||
pub mod mastodon_id;
|
||||
pub mod meta;
|
||||
pub mod nyaify;
|
||||
pub mod password;
|
||||
pub mod reaction;
|
||||
|
|
69
packages/backend-rs/src/misc/password.rs
Normal file
69
packages/backend-rs/src/misc/password.rs
Normal 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());
|
||||
}
|
||||
}
|
191
packages/backend-rs/src/misc/reaction.rs
Normal file
191
packages/backend-rs/src/misc/reaction.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||
|
||||
use super::sea_orm_active_enums::DriveFileUsageHintEnum;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
|
@ -52,6 +53,8 @@ pub struct Model {
|
|||
pub request_headers: Option<Json>,
|
||||
#[sea_orm(column_name = "requestIp")]
|
||||
pub request_ip: Option<String>,
|
||||
#[sea_orm(column_name = "usageHint")]
|
||||
pub usage_hint: Option<DriveFileUsageHintEnum>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
|
|
@ -21,6 +21,7 @@ pub struct Model {
|
|||
#[sea_orm(column_type = "Text", nullable)]
|
||||
pub text: Option<String>,
|
||||
pub name: Option<String>,
|
||||
#[sea_orm(column_type = "Text", nullable)]
|
||||
pub cw: Option<String>,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
|
|
|
@ -4,7 +4,7 @@ use sea_orm::entity::prelude::*;
|
|||
|
||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi)]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
|
||||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "antenna_src_enum")]
|
||||
pub enum AntennaSrcEnum {
|
||||
#[sea_orm(string_value = "all")]
|
||||
|
@ -22,7 +22,21 @@ pub enum AntennaSrcEnum {
|
|||
}
|
||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi)]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
enum_name = "drive_file_usage_hint_enum"
|
||||
)]
|
||||
pub enum DriveFileUsageHintEnum {
|
||||
#[sea_orm(string_value = "userAvatar")]
|
||||
UserAvatar,
|
||||
#[sea_orm(string_value = "userBanner")]
|
||||
UserBanner,
|
||||
}
|
||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
|
@ -40,7 +54,7 @@ pub enum MutedNoteReasonEnum {
|
|||
}
|
||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi)]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
|
@ -60,7 +74,7 @@ pub enum NoteVisibilityEnum {
|
|||
}
|
||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi)]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
|
@ -94,7 +108,7 @@ pub enum NotificationTypeEnum {
|
|||
}
|
||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi)]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
|
@ -110,7 +124,7 @@ pub enum PageVisibilityEnum {
|
|||
}
|
||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi)]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
|
@ -128,7 +142,7 @@ pub enum PollNotevisibilityEnum {
|
|||
}
|
||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi)]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
|
||||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "relay_status_enum")]
|
||||
pub enum RelayStatusEnum {
|
||||
#[sea_orm(string_value = "accepted")]
|
||||
|
@ -140,7 +154,7 @@ pub enum RelayStatusEnum {
|
|||
}
|
||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi)]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
|
@ -158,7 +172,7 @@ pub enum UserEmojimodpermEnum {
|
|||
}
|
||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi)]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
|
@ -174,7 +188,7 @@ pub enum UserProfileFfvisibilityEnum {
|
|||
}
|
||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi)]
|
||||
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import load from "./load.js";
|
||||
import { readEnvironmentConfig } from "backend-rs";
|
||||
|
||||
export default load();
|
||||
export const envOption = readEnvironmentConfig();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
@ -13,16 +13,15 @@ const round = (num: number) => Math.round(num * 10) / 10;
|
|||
/**
|
||||
* Report server stats regularly
|
||||
*/
|
||||
export default function () {
|
||||
export default async function () {
|
||||
const log = [] as any[];
|
||||
|
||||
ev.on("requestServerStatsLog", (x) => {
|
||||
ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length || 50));
|
||||
});
|
||||
|
||||
fetchMeta().then((meta) => {
|
||||
if (!meta.enableServerMachineStats) return;
|
||||
});
|
||||
const meta = await fetchMeta(true);
|
||||
if (!meta.enableServerMachineStats) return;
|
||||
|
||||
async function tick() {
|
||||
const cpu = await cpuUsage();
|
||||
|
|
|
@ -237,31 +237,3 @@ export async function initDb(force = false) {
|
|||
await db.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
export async function resetDb() {
|
||||
const reset = async () => {
|
||||
await redisClient.flushdb();
|
||||
const tables = await db.query(`SELECT relname AS "table"
|
||||
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
|
||||
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
|
||||
AND C.relkind = 'r'
|
||||
AND nspname !~ '^pg_toast';`);
|
||||
for (const table of tables) {
|
||||
await db.query(`DELETE FROM "${table.table}" CASCADE`);
|
||||
}
|
||||
};
|
||||
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
try {
|
||||
await reset();
|
||||
} catch (e) {
|
||||
if (i === 3) {
|
||||
throw e;
|
||||
} else {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 };
|
|
@ -0,0 +1,21 @@
|
|||
import type { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class ConvertCwVarcharToText1713225866247 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
queryRunner.query(`DROP INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f"`);
|
||||
queryRunner.query(`ALTER TABLE "note" ALTER COLUMN "cw" TYPE text`);
|
||||
queryRunner.query(
|
||||
`CREATE INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f" ON "note" USING "pgroonga" ("cw")`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
queryRunner.query(`DROP INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f"`);
|
||||
queryRunner.query(
|
||||
`ALTER TABLE "note" ALTER COLUMN "cw" TYPE character varying(512)`,
|
||||
);
|
||||
queryRunner.query(
|
||||
`CREATE INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f" ON "note" USING "pgroonga" ("cw" pgroonga_varchar_full_text_search_ops_v2)`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import type { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class AddDriveFileUsage1713451569342 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE drive_file_usage_hint_enum AS ENUM ('userAvatar', 'userBanner')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "drive_file" ADD "usageHint" drive_file_usage_hint_enum DEFAULT NULL`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "usageHint"`);
|
||||
await queryRunner.query(`DROP TYPE drive_file_usage_hint_enum`);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { redisClient } from "@/db/redis.js";
|
||||
import { encode, decode } from "msgpackr";
|
||||
import { ChainableCommander } from "ioredis";
|
||||
import type { ChainableCommander } from "ioredis";
|
||||
|
||||
export class Cache<T> {
|
||||
private ttl: number;
|
||||
|
|
|
@ -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(", ");
|
||||
}
|
|
@ -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})$`);
|
|
@ -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);
|
|
@ -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,
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
import type { Packed } from "./schema.js";
|
||||
|
||||
/**
|
||||
* 投稿を表す文字列を取得します。
|
||||
* @param {*} note (packされた)投稿
|
||||
*/
|
||||
export const getNoteSummary = (note: Packed<"Note">): string => {
|
||||
if (note.deletedAt) {
|
||||
return "❌";
|
||||
}
|
||||
|
||||
let summary = "";
|
||||
|
||||
// 本文
|
||||
if (note.cw != null) {
|
||||
summary += note.cw;
|
||||
} else {
|
||||
summary += note.text ? note.text : "";
|
||||
}
|
||||
|
||||
// ファイルが添付されているとき
|
||||
if ((note.files || []).length !== 0) {
|
||||
const len = note.files?.length;
|
||||
summary += ` 📎${len !== 1 ? ` (${len})` : ""}`;
|
||||
}
|
||||
|
||||
// 投票が添付されているとき
|
||||
if (note.poll) {
|
||||
summary += " 📊";
|
||||
}
|
||||
|
||||
/*
|
||||
// 返信のとき
|
||||
if (note.replyId) {
|
||||
if (note.reply) {
|
||||
summary += `\n\nRE: ${getNoteSummary(note.reply)}`;
|
||||
} else {
|
||||
summary += '\n\nRE: ...';
|
||||
}
|
||||
}
|
||||
|
||||
// Renoteのとき
|
||||
if (note.renoteId) {
|
||||
if (note.renote) {
|
||||
summary += `\n\nRN: ${getNoteSummary(note.renote)}`;
|
||||
} else {
|
||||
summary += '\n\nRN: ...';
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return summary.trim();
|
||||
};
|
|
@ -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");
|
||||
}
|
|
@ -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";
|
||||
|
|
|
@ -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 || ""}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export function safeForSql(text: string): boolean {
|
||||
return !/[\0\x08\x09\x1a\n\r"'\\\%]/g.test(text);
|
||||
}
|
|
@ -33,8 +33,10 @@ import { packedGalleryPostSchema } from "@/models/schema/gallery-post.js";
|
|||
import { packedEmojiSchema } from "@/models/schema/emoji.js";
|
||||
import { packedNoteEdit } from "@/models/schema/note-edit.js";
|
||||
import { packedNoteFileSchema } from "@/models/schema/note-file.js";
|
||||
import { packedAbuseUserReportSchema } from "@/models/schema/abuse-user-report.js";
|
||||
|
||||
export const refs = {
|
||||
AbuseUserReport: packedAbuseUserReportSchema,
|
||||
UserLite: packedUserLiteSchema,
|
||||
UserDetailedNotMeOnly: packedUserDetailedNotMeOnlySchema,
|
||||
MeDetailedOnly: packedMeDetailedOnlySchema,
|
||||
|
|
|
@ -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}`),
|
||||
|
|
|
@ -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)),
|
||||
);
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
export function sqlLikeEscape(s: string) {
|
||||
return s.replace(/([%_])/g, "\\$1");
|
||||
}
|
|
@ -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.");
|
||||
|
|
|
@ -16,6 +16,8 @@ import { DriveFolder } from "./drive-folder.js";
|
|||
import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js";
|
||||
import { NoteFile } from "./note-file.js";
|
||||
|
||||
export type DriveFileUsageHint = "userAvatar" | "userBanner" | null;
|
||||
|
||||
@Entity()
|
||||
@Index(["userId", "folderId", "id"])
|
||||
export class DriveFile {
|
||||
|
@ -177,6 +179,14 @@ export class DriveFile {
|
|||
})
|
||||
public isSensitive: boolean;
|
||||
|
||||
// Hint for what this file is used for
|
||||
@Column({
|
||||
type: "enum",
|
||||
enum: ["userAvatar", "userBanner"],
|
||||
nullable: true,
|
||||
})
|
||||
public usageHint: DriveFileUsageHint;
|
||||
|
||||
/**
|
||||
* 外部の(信頼されていない)URLへの直リンクか否か
|
||||
*/
|
||||
|
|
|
@ -72,9 +72,8 @@ export class Note {
|
|||
})
|
||||
public name: string | null;
|
||||
|
||||
@Index() // USING pgroonga pgroonga_varchar_full_text_search_ops_v2
|
||||
@Column("varchar", {
|
||||
length: 512,
|
||||
@Index() // USING pgroonga
|
||||
@Column("text", {
|
||||
nullable: true,
|
||||
})
|
||||
public cw: string | null;
|
||||
|
|
|
@ -2,6 +2,7 @@ import { db } from "@/db/postgre.js";
|
|||
import { Users } from "../index.js";
|
||||
import { AbuseUserReport } from "@/models/entities/abuse-user-report.js";
|
||||
import { awaitAll } from "@/prelude/await-all.js";
|
||||
import type { Packed } from "@/misc/schema.js";
|
||||
|
||||
export const AbuseUserReportRepository = db
|
||||
.getRepository(AbuseUserReport)
|
||||
|
@ -10,7 +11,7 @@ export const AbuseUserReportRepository = db
|
|||
const report =
|
||||
typeof src === "object" ? src : await this.findOneByOrFail({ id: src });
|
||||
|
||||
return await awaitAll({
|
||||
const packed: Packed<"AbuseUserReport"> = await awaitAll({
|
||||
id: report.id,
|
||||
createdAt: report.createdAt.toISOString(),
|
||||
comment: report.comment,
|
||||
|
@ -31,9 +32,10 @@ export const AbuseUserReportRepository = db
|
|||
: null,
|
||||
forwarded: report.forwarded,
|
||||
});
|
||||
return packed;
|
||||
},
|
||||
|
||||
packMany(reports: any[]) {
|
||||
packMany(reports: (AbuseUserReport["id"] | AbuseUserReport)[]) {
|
||||
return Promise.all(reports.map((x) => this.pack(x)));
|
||||
},
|
||||
});
|
||||
|
|
|
@ -40,6 +40,7 @@ export const ChannelRepository = db.getRepository(Channel).extend({
|
|||
name: channel.name,
|
||||
description: channel.description,
|
||||
userId: channel.userId,
|
||||
bannerId: channel.bannerId,
|
||||
bannerUrl: banner ? DriveFiles.getPublicUrl(banner, false) : null,
|
||||
usersCount: channel.usersCount,
|
||||
notesCount: channel.notesCount,
|
||||
|
|
|
@ -152,6 +152,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
|
|||
md5: file.md5,
|
||||
size: file.size,
|
||||
isSensitive: file.isSensitive,
|
||||
usageHint: file.usageHint,
|
||||
blurhash: file.blurhash,
|
||||
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
||||
url: opts.self ? file.url : this.getPublicUrl(file, false),
|
||||
|
@ -193,6 +194,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
|
|||
md5: file.md5,
|
||||
size: file.size,
|
||||
isSensitive: file.isSensitive,
|
||||
usageHint: file.usageHint,
|
||||
blurhash: file.blurhash,
|
||||
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
||||
url: opts.self ? file.url : this.getPublicUrl(file, false),
|
||||
|
|
|
@ -19,7 +19,9 @@ export const GalleryPostRepository = db.getRepository(GalleryPost).extend({
|
|||
createdAt: post.createdAt.toISOString(),
|
||||
updatedAt: post.updatedAt.toISOString(),
|
||||
userId: post.userId,
|
||||
user: Users.pack(post.user || post.userId, me),
|
||||
user: Users.pack(post.user || post.userId, me, {
|
||||
detail: true,
|
||||
}),
|
||||
title: post.title,
|
||||
description: post.description,
|
||||
fileIds: post.fileIds,
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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,
|
||||
|
@ -233,6 +232,7 @@ export const NoteRepository = db.getRepository(Note).extend({
|
|||
uri: note.uri || undefined,
|
||||
url: note.url || undefined,
|
||||
updatedAt: note.updatedAt?.toISOString() || undefined,
|
||||
hasPoll: note.hasPoll,
|
||||
poll: note.hasPoll ? populatePoll(note, meId) : undefined,
|
||||
...(meId
|
||||
? {
|
||||
|
|
69
packages/backend/src/models/schema/abuse-user-report.ts
Normal file
69
packages/backend/src/models/schema/abuse-user-report.ts
Normal file
|
@ -0,0 +1,69 @@
|
|||
export const packedAbuseUserReportSchema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id",
|
||||
example: "xxxxxxxxxx",
|
||||
},
|
||||
createdAt: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "date-time",
|
||||
},
|
||||
comment: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
},
|
||||
resolved: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
},
|
||||
reporterId: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id",
|
||||
},
|
||||
targetUserId: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id",
|
||||
},
|
||||
assigneeId: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
format: "id",
|
||||
},
|
||||
reporter: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
ref: "UserDetailed",
|
||||
},
|
||||
targetUser: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
ref: "UserDetailed",
|
||||
},
|
||||
assignee: {
|
||||
type: "object",
|
||||
optional: true,
|
||||
nullable: true,
|
||||
ref: "UserDetailed",
|
||||
},
|
||||
forwarded: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
},
|
||||
},
|
||||
} as const;
|
|
@ -36,6 +36,13 @@ export const packedChannelSchema = {
|
|||
nullable: true,
|
||||
optional: false,
|
||||
},
|
||||
bannerId: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
format: "id",
|
||||
example: "xxxxxxxxxx",
|
||||
},
|
||||
notesCount: {
|
||||
type: "number",
|
||||
nullable: false,
|
||||
|
@ -57,5 +64,10 @@ export const packedChannelSchema = {
|
|||
optional: false,
|
||||
format: "id",
|
||||
},
|
||||
hasUnreadNote: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
|
|
@ -44,6 +44,12 @@ export const packedDriveFileSchema = {
|
|||
optional: false,
|
||||
nullable: false,
|
||||
},
|
||||
usageHint: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
enum: ["userAvatar", "userBanner"],
|
||||
},
|
||||
blurhash: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
|
|
|
@ -38,7 +38,7 @@ export const packedGalleryPostSchema = {
|
|||
},
|
||||
user: {
|
||||
type: "object",
|
||||
ref: "UserLite",
|
||||
ref: "UserDetailed",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
},
|
||||
|
@ -79,5 +79,15 @@ export const packedGalleryPostSchema = {
|
|||
optional: false,
|
||||
nullable: false,
|
||||
},
|
||||
isLiked: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
},
|
||||
likedCount: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
|
|
@ -28,7 +28,7 @@ export const packedNoteSchema = {
|
|||
},
|
||||
cw: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
optional: false,
|
||||
nullable: true,
|
||||
},
|
||||
userId: {
|
||||
|
@ -98,7 +98,7 @@ export const packedNoteSchema = {
|
|||
},
|
||||
fileIds: {
|
||||
type: "array",
|
||||
optional: true,
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
|
@ -128,6 +128,11 @@ export const packedNoteSchema = {
|
|||
nullable: false,
|
||||
},
|
||||
},
|
||||
hasPoll: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
},
|
||||
poll: {
|
||||
type: "object",
|
||||
optional: true,
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -34,7 +34,7 @@ export function initialize<T>(name: string, limitPerSec = -1) {
|
|||
function apBackoff(attemptsMade: number, err: Error) {
|
||||
const baseDelay = 60 * 1000; // 1min
|
||||
const maxBackoff = 8 * 60 * 60 * 1000; // 8hours
|
||||
let backoff = (Math.pow(2, attemptsMade) - 1) * baseDelay;
|
||||
let backoff = (2 ** attemptsMade - 1) * baseDelay;
|
||||
backoff = Math.min(backoff, maxBackoff);
|
||||
backoff += Math.round(backoff * Math.random() * 0.2);
|
||||
return backoff;
|
||||
|
|
|
@ -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}`;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue