Merge branch 'develop' into feat/show-MkRemoteCaution-in-history
This commit is contained in:
commit
d9982a0b6a
324 changed files with 4488 additions and 2964 deletions
|
@ -178,19 +178,13 @@ logLevel: [
|
||||||
# Media Proxy
|
# Media Proxy
|
||||||
#mediaProxy: https://example.com/proxy
|
#mediaProxy: https://example.com/proxy
|
||||||
|
|
||||||
# Proxy remote files (default: false)
|
# Proxy remote files (default: true)
|
||||||
#proxyRemoteFiles: true
|
#proxyRemoteFiles: true
|
||||||
|
|
||||||
#allowedPrivateNetworks: [
|
#allowedPrivateNetworks: [
|
||||||
# '127.0.0.1/32'
|
# '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)
|
# Upload or download file size limits (bytes)
|
||||||
#maxFileSize: 262144000
|
#maxFileSize: 262144000
|
||||||
|
|
||||||
|
|
|
@ -3,30 +3,47 @@
|
||||||
🔒 Found a security vulnerability? [Please disclose it responsibly.](https://firefish.dev/firefish/firefish/-/blob/develop/SECURITY.md)
|
🔒 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) -->
|
🤝 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)
|
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 agree to follow this project's Contribution Guidelines
|
||||||
- [ ] I have searched the issue tracker for similar issues, and this is not a duplicate.
|
- [ ] 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.
|
- [ ] 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)
|
🔒 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) -->
|
🤝 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)
|
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 agree to follow this project's Contribution Guidelines
|
||||||
- [ ] I have searched the issue tracker for similar requests, and this is not a duplicate.
|
- [ ] 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.
|
- [ ] 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). -->
|
<!-- 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)
|
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
|
- [ ] This change is reviewed in an issue / This is a minor bug fix
|
||||||
- [ ] I agree to follow this project's Contribution Guidelines
|
- [ ] 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",
|
"esbenp.prettier-vscode",
|
||||||
"redhat.vscode-yaml",
|
"redhat.vscode-yaml",
|
||||||
"yoavbls.pretty-ts-errors",
|
"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]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.16"
|
version = "0.2.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android-tzdata"
|
name = "android-tzdata"
|
||||||
|
@ -132,6 +132,18 @@ version = "1.0.82"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
|
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]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
|
@ -190,11 +202,14 @@ checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
|
||||||
name = "backend-rs"
|
name = "backend-rs"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"argon2",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"basen",
|
"basen",
|
||||||
|
"bcrypt",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"chrono",
|
"chrono",
|
||||||
"cuid2",
|
"cuid2",
|
||||||
|
"emojis",
|
||||||
"idna",
|
"idna",
|
||||||
"jsonschema",
|
"jsonschema",
|
||||||
"macro_rs",
|
"macro_rs",
|
||||||
|
@ -238,6 +253,12 @@ version = "0.21.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64ct"
|
name = "base64ct"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
|
@ -250,6 +271,19 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1dbe4bb73fd931c4d1aaf53b35d1286c8a948ad00ec92c8e3c856f15fd027f43"
|
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]]
|
[[package]]
|
||||||
name = "bigdecimal"
|
name = "bigdecimal"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -303,6 +337,15 @@ dependencies = [
|
||||||
"wyz",
|
"wyz",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake2"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
|
@ -312,6 +355,16 @@ dependencies = [
|
||||||
"generic-array",
|
"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]]
|
[[package]]
|
||||||
name = "borsh"
|
name = "borsh"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -384,9 +437,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.92"
|
version = "1.0.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41"
|
checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
@ -412,7 +465,17 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
"wasm-bindgen",
|
"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]]
|
[[package]]
|
||||||
|
@ -633,13 +696,22 @@ checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.10.0"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "emojis"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ee61eb945bff65ee7d19d157d39c67c33290ff0742907413fd5eefd29edc979"
|
||||||
|
dependencies = [
|
||||||
|
"phf",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.34"
|
version = "0.8.34"
|
||||||
|
@ -1075,6 +1147,15 @@ dependencies = [
|
||||||
"syn 2.0.58",
|
"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]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.9.0"
|
version = "2.9.0"
|
||||||
|
@ -1122,7 +1203,7 @@ checksum = "2a071f4f7efc9a9118dfb627a0a94ef247986e1ab8606a4c806ae2b3aa3b6978"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.11",
|
"ahash 0.8.11",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64 0.21.7",
|
||||||
"bytecount",
|
"bytecount",
|
||||||
"clap",
|
"clap",
|
||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
|
@ -1175,7 +1256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.52.4",
|
"windows-targets 0.52.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1296,9 +1377,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "napi-build"
|
name = "napi-build"
|
||||||
version = "2.1.2"
|
version = "2.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f9130fccc5f763cf2069b34a089a18f0d0883c66aceb81f2fad541a3d823c43"
|
checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "napi-derive"
|
name = "napi-derive"
|
||||||
|
@ -1350,9 +1431,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
|
checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-complex",
|
"num-complex",
|
||||||
|
@ -1559,6 +1640,17 @@ dependencies = [
|
||||||
"syn 2.0.58",
|
"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]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.14"
|
version = "1.0.14"
|
||||||
|
@ -1580,6 +1672,24 @@ version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
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]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
|
@ -1801,7 +1911,7 @@ version = "0.11.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
|
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64 0.21.7",
|
||||||
"bytes",
|
"bytes",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
@ -1947,7 +2057,7 @@ version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64 0.21.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2231,6 +2341,12 @@ version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
|
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "0.3.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
@ -2398,7 +2514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
|
checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64",
|
"base64 0.21.7",
|
||||||
"bigdecimal",
|
"bigdecimal",
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.5.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -2445,7 +2561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
|
checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64",
|
"base64 0.21.7",
|
||||||
"bigdecimal",
|
"bigdecimal",
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.5.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -3041,7 +3157,7 @@ version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets 0.52.4",
|
"windows-targets 0.52.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3059,7 +3175,7 @@ version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets 0.52.4",
|
"windows-targets 0.52.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3079,17 +3195,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.4"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
|
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm 0.52.4",
|
"windows_aarch64_gnullvm 0.52.5",
|
||||||
"windows_aarch64_msvc 0.52.4",
|
"windows_aarch64_msvc 0.52.5",
|
||||||
"windows_i686_gnu 0.52.4",
|
"windows_i686_gnu 0.52.5",
|
||||||
"windows_i686_msvc 0.52.4",
|
"windows_i686_gnullvm",
|
||||||
"windows_x86_64_gnu 0.52.4",
|
"windows_i686_msvc 0.52.5",
|
||||||
"windows_x86_64_gnullvm 0.52.4",
|
"windows_x86_64_gnu 0.52.5",
|
||||||
"windows_x86_64_msvc 0.52.4",
|
"windows_x86_64_gnullvm 0.52.5",
|
||||||
|
"windows_x86_64_msvc 0.52.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3100,9 +3217,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.4"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
|
@ -3112,9 +3229,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.4"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
|
@ -3124,9 +3241,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.4"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
|
@ -3136,9 +3259,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.4"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
|
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
|
@ -3148,9 +3271,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.4"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
|
@ -3160,9 +3283,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.4"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
|
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
|
@ -3172,9 +3295,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.4"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
|
|
|
@ -7,14 +7,17 @@ macro_rs = { path = "packages/macro-rs" }
|
||||||
|
|
||||||
napi = { version = "2.16.2", default-features = false }
|
napi = { version = "2.16.2", default-features = false }
|
||||||
napi-derive = "2.16.2"
|
napi-derive = "2.16.2"
|
||||||
napi-build = "2.1.2"
|
napi-build = "2.1.3"
|
||||||
|
|
||||||
|
argon2 = "0.5.3"
|
||||||
async-trait = "0.1.80"
|
async-trait = "0.1.80"
|
||||||
basen = "0.1.0"
|
basen = "0.1.0"
|
||||||
|
bcrypt = "0.15.1"
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
chrono = "0.4.37"
|
chrono = "0.4.37"
|
||||||
convert_case = "0.6.0"
|
convert_case = "0.6.0"
|
||||||
cuid2 = "0.1.2"
|
cuid2 = "0.1.2"
|
||||||
|
emojis = "0.6.1"
|
||||||
idna = "0.5.0"
|
idna = "0.5.0"
|
||||||
jsonschema = "0.17.1"
|
jsonschema = "0.17.1"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://biomejs.dev/schemas/1.6.4/schema.json",
|
"$schema": "https://biomejs.dev/schemas/1.6.4/schema.json",
|
||||||
"organizeImports": {
|
"organizeImports": {
|
||||||
"enabled": true
|
"enabled": false
|
||||||
},
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -21,7 +21,8 @@
|
||||||
"useImportType": "warn",
|
"useImportType": "warn",
|
||||||
"useShorthandFunctionType": "warn",
|
"useShorthandFunctionType": "warn",
|
||||||
"useTemplate": "warn",
|
"useTemplate": "warn",
|
||||||
"noNonNullAssertion": "off"
|
"noNonNullAssertion": "off",
|
||||||
|
"useNodejsImportProtocol": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ services:
|
||||||
# - web
|
# - web
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
|
NODE_OPTIONS: --max-old-space-size=3072
|
||||||
volumes:
|
volumes:
|
||||||
- ./custom:/firefish/custom:ro
|
- ./custom:/firefish/custom:ro
|
||||||
- ./files:/firefish/files
|
- ./files:/firefish/files
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
Breaking changes are indicated by the :warning: icon.
|
Breaking changes are indicated by the :warning: icon.
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
- Added `antennaLimit` field to the response of `meta` and `admin/meta`, and the request of `admin/update-meta` (optional).
|
||||||
|
|
||||||
## v20240413
|
## v20240413
|
||||||
|
|
||||||
- :warning: Removed `patrons` endpoint.
|
- :warning: Removed `patrons` endpoint.
|
||||||
|
|
|
@ -5,6 +5,10 @@ Critical security updates are indicated by the :warning: icon.
|
||||||
- Server administrators should check [notice-for-admins.md](./notice-for-admins.md) as well.
|
- 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.
|
- Third-party client/bot developers may want to check [api-change.md](./api-change.md) as well.
|
||||||
|
|
||||||
|
## [v20240421](https://firefish.dev/firefish/firefish/-/merge_requests/10756/commits)
|
||||||
|
|
||||||
|
- Fix bugs
|
||||||
|
|
||||||
## [v20240413](https://firefish.dev/firefish/firefish/-/merge_requests/10741/commits)
|
## [v20240413](https://firefish.dev/firefish/firefish/-/merge_requests/10741/commits)
|
||||||
|
|
||||||
- Add "Media" tab to user page
|
- Add "Media" tab to user page
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
|
||||||
DELETE FROM "migrations" WHERE name IN (
|
DELETE FROM "migrations" WHERE name IN (
|
||||||
|
'AddDriveFileUsage1713451569342',
|
||||||
|
'ConvertCwVarcharToText1713225866247',
|
||||||
'FixChatFileConstraint1712855579316',
|
'FixChatFileConstraint1712855579316',
|
||||||
'DropTimeZone1712425488543',
|
'DropTimeZone1712425488543',
|
||||||
'ExpandNoteEdit1711936358554',
|
'ExpandNoteEdit1711936358554',
|
||||||
|
@ -22,6 +24,15 @@ DELETE FROM "migrations" WHERE name IN (
|
||||||
'RemoveNativeUtilsMigration1705877093218'
|
'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
|
-- fix-chat-file-constraint
|
||||||
ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_535def119223ac05ad3fa9ef64b";
|
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;
|
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
|
1. Build
|
||||||
```sh
|
```sh
|
||||||
pnpm install --frozen-lockfile
|
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
|
1. Execute database migrations
|
||||||
```sh
|
```sh
|
||||||
|
@ -242,6 +242,7 @@ In this instruction, we use [Caddy](https://caddyserver.com/) to make the Firefi
|
||||||
WorkingDirectory=/home/firefish/firefish
|
WorkingDirectory=/home/firefish/firefish
|
||||||
Environment="NODE_ENV=production"
|
Environment="NODE_ENV=production"
|
||||||
Environment="npm_config_cache=/tmp"
|
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)
|
# 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"
|
# Environment="LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
|
|
|
@ -394,6 +394,7 @@ enableRegistration: "Enable new user registration"
|
||||||
invite: "Invite"
|
invite: "Invite"
|
||||||
driveCapacityPerLocalAccount: "Drive capacity per local user"
|
driveCapacityPerLocalAccount: "Drive capacity per local user"
|
||||||
driveCapacityPerRemoteAccount: "Drive capacity per remote user"
|
driveCapacityPerRemoteAccount: "Drive capacity per remote user"
|
||||||
|
antennaLimit: "The maximum number of antennas that each user can create"
|
||||||
inMb: "In megabytes"
|
inMb: "In megabytes"
|
||||||
iconUrl: "Icon URL"
|
iconUrl: "Icon URL"
|
||||||
bannerUrl: "Banner image URL"
|
bannerUrl: "Banner image URL"
|
||||||
|
|
|
@ -2318,3 +2318,4 @@ markLocalFilesNsfwByDefaultDescription: Indépendamment de ce réglage, les util
|
||||||
peuvent supprimer le drapeau « sensible » (NSFW) eux-mêmes. Les fichiers existants
|
peuvent supprimer le drapeau « sensible » (NSFW) eux-mêmes. Les fichiers existants
|
||||||
ne sont pas affectés.
|
ne sont pas affectés.
|
||||||
noteEditHistory: Historique des publications
|
noteEditHistory: Historique des publications
|
||||||
|
media: Multimédia
|
||||||
|
|
|
@ -340,6 +340,7 @@ invite: "邀请"
|
||||||
driveCapacityPerLocalAccount: "每个本地用户的网盘容量"
|
driveCapacityPerLocalAccount: "每个本地用户的网盘容量"
|
||||||
driveCapacityPerRemoteAccount: "每个远程用户的网盘容量"
|
driveCapacityPerRemoteAccount: "每个远程用户的网盘容量"
|
||||||
inMb: "以兆字节 (MegaByte) 为单位"
|
inMb: "以兆字节 (MegaByte) 为单位"
|
||||||
|
antennaLimit: "每个用户最多可以创建的天线数量"
|
||||||
iconUrl: "图标 URL"
|
iconUrl: "图标 URL"
|
||||||
bannerUrl: "横幅图 URL"
|
bannerUrl: "横幅图 URL"
|
||||||
backgroundImageUrl: "背景图 URL"
|
backgroundImageUrl: "背景图 URL"
|
||||||
|
|
14
package.json
14
package.json
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "firefish",
|
"name": "firefish",
|
||||||
"version": "20240413",
|
"version": "20240421",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://firefish.dev/firefish/firefish.git"
|
"url": "https://firefish.dev/firefish/firefish.git"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@8.15.6",
|
"packageManager": "pnpm@8.15.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"rebuild": "pnpm run clean && pnpm run build",
|
"rebuild": "pnpm run clean && pnpm run build",
|
||||||
|
@ -26,7 +26,9 @@
|
||||||
"debug": "pnpm run build:debug && pnpm run start",
|
"debug": "pnpm run build:debug && pnpm run start",
|
||||||
"build:debug": "pnpm run clean && pnpm node ./scripts/dev-build.mjs && pnpm run gulp",
|
"build:debug": "pnpm run clean && pnpm node ./scripts/dev-build.mjs && pnpm run gulp",
|
||||||
"mocha": "pnpm --filter backend run mocha",
|
"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": "pnpm run format:ts; pnpm run format:rs",
|
||||||
"format:ts": "pnpm -r --parallel run format",
|
"format:ts": "pnpm -r --parallel run format",
|
||||||
"format:rs": "cargo fmt --all --",
|
"format:rs": "cargo fmt --all --",
|
||||||
|
@ -36,11 +38,11 @@
|
||||||
"clean-all": "pnpm run clean && pnpm run clean-cargo && pnpm run clean-npm"
|
"clean-all": "pnpm run clean && pnpm run clean-cargo && pnpm run clean-npm"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-yaml": "4.1.0",
|
|
||||||
"gulp": "4.0.2",
|
"gulp": "4.0.2",
|
||||||
"gulp-cssnano": "2.1.3",
|
"gulp-cssnano": "2.1.3",
|
||||||
"gulp-replace": "1.1.4",
|
"gulp-replace": "1.1.4",
|
||||||
"gulp-terser": "2.1.0"
|
"gulp-terser": "2.1.0",
|
||||||
|
"js-yaml": "4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.6.4",
|
"@biomejs/biome": "1.6.4",
|
||||||
|
@ -50,7 +52,7 @@
|
||||||
"@biomejs/cli-linux-x64": "^1.6.4",
|
"@biomejs/cli-linux-x64": "^1.6.4",
|
||||||
"@types/node": "20.12.7",
|
"@types/node": "20.12.7",
|
||||||
"execa": "8.0.1",
|
"execa": "8.0.1",
|
||||||
"pnpm": "8.15.6",
|
"pnpm": "8.15.7",
|
||||||
"typescript": "5.4.5"
|
"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`: Main backend code written in TypeScript for NodeJS
|
||||||
- `backend-rs`: Backend code written in Rust, bound to NodeJS by [NAPI-RS](https://napi.rs/)
|
- `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
|
- `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
|
- `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
|
- `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 = { workspace = true, optional = true, default-features = false, features = ["napi9", "tokio_rt", "chrono_date", "serde-json"] }
|
||||||
napi-derive = { workspace = true, optional = true }
|
napi-derive = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
argon2 = { workspace = true, features = ["std"] }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
basen = { workspace = true }
|
basen = { workspace = true }
|
||||||
|
bcrypt = { workspace = true }
|
||||||
cfg-if = { workspace = true }
|
cfg-if = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
cuid2 = { workspace = true }
|
cuid2 = { workspace = true }
|
||||||
|
emojis = { workspace = true }
|
||||||
idna = { workspace = true }
|
idna = { workspace = true }
|
||||||
jsonschema = { workspace = true }
|
jsonschema = { workspace = true }
|
||||||
once_cell = { workspace = true }
|
once_cell = { workspace = true }
|
||||||
|
|
|
@ -17,7 +17,7 @@ regenerate-entities:
|
||||||
attribute=$$(printf 'cfg_attr(feature = "napi", napi_derive::napi(object, js_name = "%s", use_nullable = true))' "$${jsname}"); \
|
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}"; \
|
sed -i "s/NAPI_EXTRA_ATTR_PLACEHOLDER/$${attribute}/" "$${file}"; \
|
||||||
done
|
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
|
src/model/entity/sea_orm_active_enums.rs
|
||||||
cargo fmt --all --
|
cargo fmt --all --
|
||||||
|
|
||||||
|
|
190
packages/backend-rs/index.d.ts
vendored
190
packages/backend-rs/index.d.ts
vendored
|
@ -3,6 +3,16 @@
|
||||||
|
|
||||||
/* auto-generated by NAPI-RS */
|
/* 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 {
|
export interface ServerConfig {
|
||||||
url: string
|
url: string
|
||||||
port: number
|
port: number
|
||||||
|
@ -118,7 +128,8 @@ export interface Acct {
|
||||||
}
|
}
|
||||||
export function stringToAcct(acct: string): Acct
|
export function stringToAcct(acct: string): Acct
|
||||||
export function acctToString(acct: Acct): string
|
export function acctToString(acct: Acct): string
|
||||||
export interface NoteLike {
|
/** TODO: handle name collisions better */
|
||||||
|
export interface NoteLikeForCheckWordMute {
|
||||||
fileIds: Array<string>
|
fileIds: Array<string>
|
||||||
userId: string | null
|
userId: string | null
|
||||||
text: string | null
|
text: string | null
|
||||||
|
@ -126,15 +137,52 @@ export interface NoteLike {
|
||||||
renoteId: string | null
|
renoteId: string | null
|
||||||
replyId: 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 getFullApAccount(username: string, host?: string | undefined | null): string
|
||||||
export function isSelfHost(host?: string | undefined | null): boolean
|
export function isSelfHost(host?: string | undefined | null): boolean
|
||||||
export function isSameOrigin(uri: string): boolean
|
export function isSameOrigin(uri: string): boolean
|
||||||
export function extractHost(uri: string): string
|
export function extractHost(uri: string): string
|
||||||
export function toPuny(host: 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 toMastodonId(firefishId: string): string | null
|
||||||
export function fromMastodonId(mastodonId: 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 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 {
|
export interface AbuseUserReport {
|
||||||
id: string
|
id: string
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
@ -300,6 +348,7 @@ export interface DriveFile {
|
||||||
webpublicType: string | null
|
webpublicType: string | null
|
||||||
requestHeaders: Json | null
|
requestHeaders: Json | null
|
||||||
requestIp: string | null
|
requestIp: string | null
|
||||||
|
usageHint: DriveFileUsageHintEnum | null
|
||||||
}
|
}
|
||||||
export interface DriveFolder {
|
export interface DriveFolder {
|
||||||
id: string
|
id: string
|
||||||
|
@ -443,6 +492,7 @@ export interface Meta {
|
||||||
recaptchaSecretKey: string | null
|
recaptchaSecretKey: string | null
|
||||||
localDriveCapacityMb: number
|
localDriveCapacityMb: number
|
||||||
remoteDriveCapacityMb: number
|
remoteDriveCapacityMb: number
|
||||||
|
antennaLimit: number
|
||||||
summalyProxy: string | null
|
summalyProxy: string | null
|
||||||
enableEmail: boolean
|
enableEmail: boolean
|
||||||
email: string | null
|
email: string | null
|
||||||
|
@ -724,81 +774,85 @@ export interface ReplyMuting {
|
||||||
muteeId: string
|
muteeId: string
|
||||||
muterId: string
|
muterId: string
|
||||||
}
|
}
|
||||||
export const enum AntennaSrcEnum {
|
export enum AntennaSrcEnum {
|
||||||
All = 0,
|
All = 'all',
|
||||||
Group = 1,
|
Group = 'group',
|
||||||
Home = 2,
|
Home = 'home',
|
||||||
Instances = 3,
|
Instances = 'instances',
|
||||||
List = 4,
|
List = 'list',
|
||||||
Users = 5
|
Users = 'users'
|
||||||
}
|
}
|
||||||
export const enum MutedNoteReasonEnum {
|
export enum DriveFileUsageHintEnum {
|
||||||
Manual = 0,
|
UserAvatar = 'userAvatar',
|
||||||
Other = 1,
|
UserBanner = 'userBanner'
|
||||||
Spam = 2,
|
|
||||||
Word = 3
|
|
||||||
}
|
}
|
||||||
export const enum NoteVisibilityEnum {
|
export enum MutedNoteReasonEnum {
|
||||||
Followers = 0,
|
Manual = 'manual',
|
||||||
Hidden = 1,
|
Other = 'other',
|
||||||
Home = 2,
|
Spam = 'spam',
|
||||||
Public = 3,
|
Word = 'word'
|
||||||
Specified = 4
|
|
||||||
}
|
}
|
||||||
export const enum NotificationTypeEnum {
|
export enum NoteVisibilityEnum {
|
||||||
App = 0,
|
Followers = 'followers',
|
||||||
Follow = 1,
|
Hidden = 'hidden',
|
||||||
FollowRequestAccepted = 2,
|
Home = 'home',
|
||||||
GroupInvited = 3,
|
Public = 'public',
|
||||||
Mention = 4,
|
Specified = 'specified'
|
||||||
PollEnded = 5,
|
|
||||||
PollVote = 6,
|
|
||||||
Quote = 7,
|
|
||||||
Reaction = 8,
|
|
||||||
ReceiveFollowRequest = 9,
|
|
||||||
Renote = 10,
|
|
||||||
Reply = 11
|
|
||||||
}
|
}
|
||||||
export const enum PageVisibilityEnum {
|
export enum NotificationTypeEnum {
|
||||||
Followers = 0,
|
App = 'app',
|
||||||
Public = 1,
|
Follow = 'follow',
|
||||||
Specified = 2
|
FollowRequestAccepted = 'followRequestAccepted',
|
||||||
|
GroupInvited = 'groupInvited',
|
||||||
|
Mention = 'mention',
|
||||||
|
PollEnded = 'pollEnded',
|
||||||
|
PollVote = 'pollVote',
|
||||||
|
Quote = 'quote',
|
||||||
|
Reaction = 'reaction',
|
||||||
|
ReceiveFollowRequest = 'receiveFollowRequest',
|
||||||
|
Renote = 'renote',
|
||||||
|
Reply = 'reply'
|
||||||
}
|
}
|
||||||
export const enum PollNotevisibilityEnum {
|
export enum PageVisibilityEnum {
|
||||||
Followers = 0,
|
Followers = 'followers',
|
||||||
Home = 1,
|
Public = 'public',
|
||||||
Public = 2,
|
Specified = 'specified'
|
||||||
Specified = 3
|
|
||||||
}
|
}
|
||||||
export const enum RelayStatusEnum {
|
export enum PollNotevisibilityEnum {
|
||||||
Accepted = 0,
|
Followers = 'followers',
|
||||||
Rejected = 1,
|
Home = 'home',
|
||||||
Requesting = 2
|
Public = 'public',
|
||||||
|
Specified = 'specified'
|
||||||
}
|
}
|
||||||
export const enum UserEmojimodpermEnum {
|
export enum RelayStatusEnum {
|
||||||
Add = 0,
|
Accepted = 'accepted',
|
||||||
Full = 1,
|
Rejected = 'rejected',
|
||||||
Mod = 2,
|
Requesting = 'requesting'
|
||||||
Unauthorized = 3
|
|
||||||
}
|
}
|
||||||
export const enum UserProfileFfvisibilityEnum {
|
export enum UserEmojimodpermEnum {
|
||||||
Followers = 0,
|
Add = 'add',
|
||||||
Private = 1,
|
Full = 'full',
|
||||||
Public = 2
|
Mod = 'mod',
|
||||||
|
Unauthorized = 'unauthorized'
|
||||||
}
|
}
|
||||||
export const enum UserProfileMutingnotificationtypesEnum {
|
export enum UserProfileFfvisibilityEnum {
|
||||||
App = 0,
|
Followers = 'followers',
|
||||||
Follow = 1,
|
Private = 'private',
|
||||||
FollowRequestAccepted = 2,
|
Public = 'public'
|
||||||
GroupInvited = 3,
|
}
|
||||||
Mention = 4,
|
export enum UserProfileMutingnotificationtypesEnum {
|
||||||
PollEnded = 5,
|
App = 'app',
|
||||||
PollVote = 6,
|
Follow = 'follow',
|
||||||
Quote = 7,
|
FollowRequestAccepted = 'followRequestAccepted',
|
||||||
Reaction = 8,
|
GroupInvited = 'groupInvited',
|
||||||
ReceiveFollowRequest = 9,
|
Mention = 'mention',
|
||||||
Renote = 10,
|
PollEnded = 'pollEnded',
|
||||||
Reply = 11
|
PollVote = 'pollVote',
|
||||||
|
Quote = 'quote',
|
||||||
|
Reaction = 'reaction',
|
||||||
|
ReceiveFollowRequest = 'receiveFollowRequest',
|
||||||
|
Renote = 'renote',
|
||||||
|
Reply = 'reply'
|
||||||
}
|
}
|
||||||
export interface Signin {
|
export interface Signin {
|
||||||
id: string
|
id: string
|
||||||
|
|
|
@ -310,8 +310,9 @@ if (!nativeBinding) {
|
||||||
throw new Error(`Failed to load native binding`)
|
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.readServerConfig = readServerConfig
|
||||||
module.exports.stringToAcct = stringToAcct
|
module.exports.stringToAcct = stringToAcct
|
||||||
module.exports.acctToString = acctToString
|
module.exports.acctToString = acctToString
|
||||||
|
@ -321,10 +322,24 @@ module.exports.isSelfHost = isSelfHost
|
||||||
module.exports.isSameOrigin = isSameOrigin
|
module.exports.isSameOrigin = isSameOrigin
|
||||||
module.exports.extractHost = extractHost
|
module.exports.extractHost = extractHost
|
||||||
module.exports.toPuny = toPuny
|
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.toMastodonId = toMastodonId
|
||||||
module.exports.fromMastodonId = fromMastodonId
|
module.exports.fromMastodonId = fromMastodonId
|
||||||
|
module.exports.fetchMeta = fetchMeta
|
||||||
|
module.exports.metaToPugArgs = metaToPugArgs
|
||||||
module.exports.nyaify = nyaify
|
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.AntennaSrcEnum = AntennaSrcEnum
|
||||||
|
module.exports.DriveFileUsageHintEnum = DriveFileUsageHintEnum
|
||||||
module.exports.MutedNoteReasonEnum = MutedNoteReasonEnum
|
module.exports.MutedNoteReasonEnum = MutedNoteReasonEnum
|
||||||
module.exports.NoteVisibilityEnum = NoteVisibilityEnum
|
module.exports.NoteVisibilityEnum = NoteVisibilityEnum
|
||||||
module.exports.NotificationTypeEnum = NotificationTypeEnum
|
module.exports.NotificationTypeEnum = NotificationTypeEnum
|
||||||
|
|
|
@ -33,8 +33,8 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"artifacts": "napi artifacts",
|
"artifacts": "napi artifacts",
|
||||||
"build": "napi build --features napi --platform --release ./built/",
|
"build": "napi build --features napi --no-const-enum --platform --release ./built/",
|
||||||
"build:debug": "napi build --features napi --platform ./built/",
|
"build:debug": "napi build --features napi --no-const-enum --platform ./built/",
|
||||||
"prepublishOnly": "napi prepublish -t npm",
|
"prepublishOnly": "napi prepublish -t npm",
|
||||||
"test": "pnpm run cargo:test && pnpm run build:debug && ava",
|
"test": "pnpm run cargo:test && pnpm run build:debug && ava",
|
||||||
"universal": "napi universal",
|
"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;
|
pub mod server;
|
||||||
|
|
|
@ -4,7 +4,8 @@ use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use sea_orm::{prelude::*, QuerySelect};
|
use sea_orm::{prelude::*, QuerySelect};
|
||||||
|
|
||||||
#[crate::export(object)]
|
/// TODO: handle name collisions better
|
||||||
|
#[crate::export(object, js_name = "NoteLikeForCheckWordMute")]
|
||||||
pub struct NoteLike {
|
pub struct NoteLike {
|
||||||
pub file_ids: Vec<String>,
|
pub file_ids: Vec<String>,
|
||||||
pub user_id: Option<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 acct;
|
||||||
pub mod check_word_mute;
|
pub mod check_word_mute;
|
||||||
pub mod convert_host;
|
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 mastodon_id;
|
||||||
|
pub mod meta;
|
||||||
pub mod nyaify;
|
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
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
|
||||||
|
|
||||||
|
use super::sea_orm_active_enums::DriveFileUsageHintEnum;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||||
|
@ -52,6 +53,8 @@ pub struct Model {
|
||||||
pub request_headers: Option<Json>,
|
pub request_headers: Option<Json>,
|
||||||
#[sea_orm(column_name = "requestIp")]
|
#[sea_orm(column_name = "requestIp")]
|
||||||
pub request_ip: Option<String>,
|
pub request_ip: Option<String>,
|
||||||
|
#[sea_orm(column_name = "usageHint")]
|
||||||
|
pub usage_hint: Option<DriveFileUsageHintEnum>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
|
|
@ -173,6 +173,8 @@ pub struct Model {
|
||||||
pub more_urls: Json,
|
pub more_urls: Json,
|
||||||
#[sea_orm(column_name = "markLocalFilesNsfwByDefault")]
|
#[sea_orm(column_name = "markLocalFilesNsfwByDefault")]
|
||||||
pub mark_local_files_nsfw_by_default: bool,
|
pub mark_local_files_nsfw_by_default: bool,
|
||||||
|
#[sea_orm(column_name = "antennaLimit")]
|
||||||
|
pub antenna_limit: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub struct Model {
|
||||||
#[sea_orm(column_type = "Text", nullable)]
|
#[sea_orm(column_type = "Text", nullable)]
|
||||||
pub text: Option<String>,
|
pub text: Option<String>,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
|
#[sea_orm(column_type = "Text", nullable)]
|
||||||
pub cw: Option<String>,
|
pub cw: Option<String>,
|
||||||
#[sea_orm(column_name = "userId")]
|
#[sea_orm(column_name = "userId")]
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
#[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")]
|
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "antenna_src_enum")]
|
||||||
pub enum AntennaSrcEnum {
|
pub enum AntennaSrcEnum {
|
||||||
#[sea_orm(string_value = "all")]
|
#[sea_orm(string_value = "all")]
|
||||||
|
@ -22,7 +22,21 @@ pub enum AntennaSrcEnum {
|
||||||
}
|
}
|
||||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
#[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(
|
#[sea_orm(
|
||||||
rs_type = "String",
|
rs_type = "String",
|
||||||
db_type = "Enum",
|
db_type = "Enum",
|
||||||
|
@ -40,7 +54,7 @@ pub enum MutedNoteReasonEnum {
|
||||||
}
|
}
|
||||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
#[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(
|
#[sea_orm(
|
||||||
rs_type = "String",
|
rs_type = "String",
|
||||||
db_type = "Enum",
|
db_type = "Enum",
|
||||||
|
@ -60,7 +74,7 @@ pub enum NoteVisibilityEnum {
|
||||||
}
|
}
|
||||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
#[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(
|
#[sea_orm(
|
||||||
rs_type = "String",
|
rs_type = "String",
|
||||||
db_type = "Enum",
|
db_type = "Enum",
|
||||||
|
@ -94,7 +108,7 @@ pub enum NotificationTypeEnum {
|
||||||
}
|
}
|
||||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
#[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(
|
#[sea_orm(
|
||||||
rs_type = "String",
|
rs_type = "String",
|
||||||
db_type = "Enum",
|
db_type = "Enum",
|
||||||
|
@ -110,7 +124,7 @@ pub enum PageVisibilityEnum {
|
||||||
}
|
}
|
||||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
#[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(
|
#[sea_orm(
|
||||||
rs_type = "String",
|
rs_type = "String",
|
||||||
db_type = "Enum",
|
db_type = "Enum",
|
||||||
|
@ -128,7 +142,7 @@ pub enum PollNotevisibilityEnum {
|
||||||
}
|
}
|
||||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
#[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")]
|
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "relay_status_enum")]
|
||||||
pub enum RelayStatusEnum {
|
pub enum RelayStatusEnum {
|
||||||
#[sea_orm(string_value = "accepted")]
|
#[sea_orm(string_value = "accepted")]
|
||||||
|
@ -140,7 +154,7 @@ pub enum RelayStatusEnum {
|
||||||
}
|
}
|
||||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
#[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(
|
#[sea_orm(
|
||||||
rs_type = "String",
|
rs_type = "String",
|
||||||
db_type = "Enum",
|
db_type = "Enum",
|
||||||
|
@ -158,7 +172,7 @@ pub enum UserEmojimodpermEnum {
|
||||||
}
|
}
|
||||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
#[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(
|
#[sea_orm(
|
||||||
rs_type = "String",
|
rs_type = "String",
|
||||||
db_type = "Enum",
|
db_type = "Enum",
|
||||||
|
@ -174,7 +188,7 @@ pub enum UserProfileFfvisibilityEnum {
|
||||||
}
|
}
|
||||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||||
#[cfg_attr(not(feature = "napi"), derive(Clone))]
|
#[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(
|
#[sea_orm(
|
||||||
rs_type = "String",
|
rs_type = "String",
|
||||||
db_type = "Enum",
|
db_type = "Enum",
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
"@swc/core-android-arm64": "1.3.11"
|
"@swc/core-android-arm64": "1.3.11"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bull-board/api": "5.15.3",
|
"@bull-board/api": "5.15.5",
|
||||||
"@bull-board/koa": "5.15.3",
|
"@bull-board/koa": "5.15.5",
|
||||||
"@bull-board/ui": "5.15.3",
|
"@bull-board/ui": "5.15.5",
|
||||||
"@discordapp/twemoji": "^15.0.3",
|
"@discordapp/twemoji": "^15.0.3",
|
||||||
"@koa/cors": "5.0.0",
|
"@koa/cors": "5.0.0",
|
||||||
"@koa/multer": "3.0.2",
|
"@koa/multer": "3.0.2",
|
||||||
|
@ -33,15 +33,12 @@
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@redocly/openapi-core": "1.11.0",
|
"@redocly/openapi-core": "1.11.0",
|
||||||
"@sinonjs/fake-timers": "11.2.2",
|
"@sinonjs/fake-timers": "11.2.2",
|
||||||
"@twemoji/parser": "^15.1.1",
|
|
||||||
"adm-zip": "0.5.10",
|
"adm-zip": "0.5.10",
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.12.0",
|
||||||
"archiver": "7.0.1",
|
"archiver": "7.0.1",
|
||||||
"argon2": "^0.40.1",
|
"aws-sdk": "2.1599.0",
|
||||||
"aws-sdk": "2.1597.0",
|
|
||||||
"axios": "^1.6.8",
|
"axios": "^1.6.8",
|
||||||
"backend-rs": "workspace:*",
|
"backend-rs": "workspace:*",
|
||||||
"bcryptjs": "2.4.3",
|
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"bull": "4.12.2",
|
"bull": "4.12.2",
|
||||||
"cacheable-lookup": "TheEssem/cacheable-lookup",
|
"cacheable-lookup": "TheEssem/cacheable-lookup",
|
||||||
|
@ -54,7 +51,7 @@
|
||||||
"date-fns": "3.6.0",
|
"date-fns": "3.6.0",
|
||||||
"decompress": "^4.2.1",
|
"decompress": "^4.2.1",
|
||||||
"deep-email-validator": "0.1.21",
|
"deep-email-validator": "0.1.21",
|
||||||
"deepl-node": "1.12.0",
|
"deepl-node": "1.13.0",
|
||||||
"escape-regexp": "0.0.1",
|
"escape-regexp": "0.0.1",
|
||||||
"feed": "4.2.2",
|
"feed": "4.2.2",
|
||||||
"file-type": "19.0.0",
|
"file-type": "19.0.0",
|
||||||
|
@ -100,7 +97,7 @@
|
||||||
"punycode": "2.3.1",
|
"punycode": "2.3.1",
|
||||||
"pureimage": "0.4.13",
|
"pureimage": "0.4.13",
|
||||||
"qrcode": "1.5.3",
|
"qrcode": "1.5.3",
|
||||||
"qs": "6.12.0",
|
"qs": "6.12.1",
|
||||||
"random-seed": "0.3.0",
|
"random-seed": "0.3.0",
|
||||||
"ratelimiter": "3.4.1",
|
"ratelimiter": "3.4.1",
|
||||||
"redis-semaphore": "5.5.1",
|
"redis-semaphore": "5.5.1",
|
||||||
|
@ -130,7 +127,6 @@
|
||||||
"@swc/cli": "0.3.12",
|
"@swc/cli": "0.3.12",
|
||||||
"@swc/core": "1.4.13",
|
"@swc/core": "1.4.13",
|
||||||
"@types/adm-zip": "^0.5.5",
|
"@types/adm-zip": "^0.5.5",
|
||||||
"@types/bcryptjs": "2.4.6",
|
|
||||||
"@types/color-convert": "^2.0.3",
|
"@types/color-convert": "^2.0.3",
|
||||||
"@types/content-disposition": "^0.5.8",
|
"@types/content-disposition": "^0.5.8",
|
||||||
"@types/escape-regexp": "0.0.3",
|
"@types/escape-regexp": "0.0.3",
|
||||||
|
|
|
@ -3,7 +3,7 @@ import chalk from "chalk";
|
||||||
import Xev from "xev";
|
import Xev from "xev";
|
||||||
|
|
||||||
import Logger from "@/services/logger.js";
|
import Logger from "@/services/logger.js";
|
||||||
import { envOption } from "../env.js";
|
import { envOption } from "@/config/index.js";
|
||||||
import { inspect } from "node:util";
|
import { inspect } from "node:util";
|
||||||
|
|
||||||
// for typeorm
|
// for typeorm
|
||||||
|
@ -76,9 +76,7 @@ cluster.on("exit", (worker) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Display detail of unhandled promise rejection
|
// Display detail of unhandled promise rejection
|
||||||
if (!envOption.quiet) {
|
process.on("unhandledRejection", console.dir);
|
||||||
process.on("unhandledRejection", console.dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display detail of uncaught exception
|
// Display detail of uncaught exception
|
||||||
process.on("uncaughtException", (err) => {
|
process.on("uncaughtException", (err) => {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import semver from "semver";
|
||||||
import Logger from "@/services/logger.js";
|
import Logger from "@/services/logger.js";
|
||||||
import loadConfig from "@/config/load.js";
|
import loadConfig from "@/config/load.js";
|
||||||
import type { Config } from "@/config/types.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 { showMachineInfo } from "@/misc/show-machine-info.js";
|
||||||
import { db, initDb } from "@/db/postgre.js";
|
import { db, initDb } from "@/db/postgre.js";
|
||||||
import { inspect } from "node:util";
|
import { inspect } from "node:util";
|
||||||
|
@ -28,58 +28,56 @@ const bootLogger = logger.createSubLogger("boot", "magenta", false);
|
||||||
const themeColor = chalk.hex("#31748f");
|
const themeColor = chalk.hex("#31748f");
|
||||||
|
|
||||||
function greet() {
|
function greet() {
|
||||||
if (!envOption.quiet) {
|
//#region Firefish logo
|
||||||
//#region Firefish logo
|
console.log(
|
||||||
console.log(
|
themeColor(
|
||||||
themeColor(
|
"██████╗ ██╗██████╗ ███████╗███████╗██╗███████╗██╗ ██╗ ○ ▄ ▄ ",
|
||||||
"██████╗ ██╗██████╗ ███████╗███████╗██╗███████╗██╗ ██╗ ○ ▄ ▄ ",
|
),
|
||||||
),
|
);
|
||||||
);
|
console.log(
|
||||||
console.log(
|
themeColor(
|
||||||
themeColor(
|
"██╔════╝██║██╔══██╗██╔════╝██╔════╝██║██╔════╝██║ ██║ ⚬ █▄▄ █▄▄ ",
|
||||||
"██╔════╝██║██╔══██╗██╔════╝██╔════╝██║██╔════╝██║ ██║ ⚬ █▄▄ █▄▄ ",
|
),
|
||||||
),
|
);
|
||||||
);
|
console.log(
|
||||||
console.log(
|
themeColor(
|
||||||
themeColor(
|
"█████╗ ██║██████╔╝█████╗ █████╗ ██║███████╗███████║ ▄▄▄▄▄▄ ▄ ",
|
||||||
"█████╗ ██║██████╔╝█████╗ █████╗ ██║███████╗███████║ ▄▄▄▄▄▄ ▄ ",
|
),
|
||||||
),
|
);
|
||||||
);
|
console.log(
|
||||||
console.log(
|
themeColor(
|
||||||
themeColor(
|
"██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║╚════██║██╔══██║ █ █ █▄▄ ",
|
||||||
"██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║╚════██║██╔══██║ █ █ █▄▄ ",
|
),
|
||||||
),
|
);
|
||||||
);
|
console.log(
|
||||||
console.log(
|
themeColor(
|
||||||
themeColor(
|
"██║ ██║██║ ██║███████╗██║ ██║███████║██║ ██║ █ ● ● █ ",
|
||||||
"██║ ██║██║ ██║███████╗██║ ██║███████║██║ ██║ █ ● ● █ ",
|
),
|
||||||
),
|
);
|
||||||
);
|
console.log(
|
||||||
console.log(
|
themeColor(
|
||||||
themeColor(
|
"╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ▀▄▄▄▄▄▄▀ ",
|
||||||
"╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ▀▄▄▄▄▄▄▀ ",
|
),
|
||||||
),
|
);
|
||||||
);
|
//#endregion
|
||||||
//#endregion
|
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
" Firefish is an open-source decentralized microblogging platform.",
|
" Firefish is an open-source decentralized microblogging platform.",
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
chalk.rgb(
|
chalk.rgb(
|
||||||
255,
|
255,
|
||||||
136,
|
136,
|
||||||
0,
|
0,
|
||||||
)(
|
)(
|
||||||
" If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish",
|
" If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("");
|
console.log("");
|
||||||
console.log(
|
console.log(
|
||||||
chalkTemplate`--- ${os.hostname()} {gray (PID: ${process.pid.toString()})} ---`,
|
chalkTemplate`--- ${os.hostname()} {gray (PID: ${process.pid.toString()})} ---`,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
bootLogger.info("Welcome to Firefish!");
|
bootLogger.info("Welcome to Firefish!");
|
||||||
bootLogger.info(`Firefish v${meta.version}`, null, true);
|
bootLogger.info(`Firefish v${meta.version}`, null, true);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
import load from "./load.js";
|
import load from "./load.js";
|
||||||
|
import { readEnvironmentConfig } from "backend-rs";
|
||||||
|
|
||||||
export default load();
|
export default load();
|
||||||
|
export const envOption = readEnvironmentConfig();
|
||||||
|
|
|
@ -55,6 +55,7 @@ export default function load() {
|
||||||
mixin.userAgent = `Firefish/${meta.version} (${config.url})`;
|
mixin.userAgent = `Firefish/${meta.version} (${config.url})`;
|
||||||
mixin.clientEntry = clientManifest["src/init.ts"];
|
mixin.clientEntry = clientManifest["src/init.ts"];
|
||||||
|
|
||||||
|
if (config.proxyRemoteFiles == null) config.proxyRemoteFiles = true;
|
||||||
if (!config.redis.prefix) config.redis.prefix = mixin.hostname;
|
if (!config.redis.prefix) config.redis.prefix = mixin.hostname;
|
||||||
if (config.cacheServer && !config.cacheServer.prefix)
|
if (config.cacheServer && !config.cacheServer.prefix)
|
||||||
config.cacheServer.prefix = mixin.hostname;
|
config.cacheServer.prefix = mixin.hostname;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import si from "systeminformation";
|
import si from "systeminformation";
|
||||||
import Xev from "xev";
|
import Xev from "xev";
|
||||||
import * as osUtils from "os-utils";
|
import * as osUtils from "os-utils";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "backend-rs";
|
||||||
|
|
||||||
const ev = new Xev();
|
const ev = new Xev();
|
||||||
|
|
||||||
|
@ -13,16 +13,15 @@ const round = (num: number) => Math.round(num * 10) / 10;
|
||||||
/**
|
/**
|
||||||
* Report server stats regularly
|
* Report server stats regularly
|
||||||
*/
|
*/
|
||||||
export default function () {
|
export default async function () {
|
||||||
const log = [] as any[];
|
const log = [] as any[];
|
||||||
|
|
||||||
ev.on("requestServerStatsLog", (x) => {
|
ev.on("requestServerStatsLog", (x) => {
|
||||||
ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length || 50));
|
ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length || 50));
|
||||||
});
|
});
|
||||||
|
|
||||||
fetchMeta().then((meta) => {
|
const meta = await fetchMeta(true);
|
||||||
if (!meta.enableServerMachineStats) return;
|
if (!meta.enableServerMachineStats) return;
|
||||||
});
|
|
||||||
|
|
||||||
async function tick() {
|
async function tick() {
|
||||||
const cpu = await cpuUsage();
|
const cpu = await cpuUsage();
|
||||||
|
|
|
@ -237,31 +237,3 @@ export async function initDb(force = false) {
|
||||||
await db.initialize();
|
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 };
|
|
19
packages/backend/src/migration/1712937600000-antennaLimit.ts
Normal file
19
packages/backend/src/migration/1712937600000-antennaLimit.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import type { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class antennaLimit1712937600000 implements MigrationInterface {
|
||||||
|
async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "meta" ADD "antennaLimit" integer NOT NULL DEFAULT 5`,
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`COMMENT ON COLUMN "meta"."antennaLimit" IS 'Antenna Limit'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "meta" DROP COLUMN "antennaLimit"`,
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 { redisClient } from "@/db/redis.js";
|
||||||
import { encode, decode } from "msgpackr";
|
import { encode, decode } from "msgpackr";
|
||||||
import { ChainableCommander } from "ioredis";
|
import type { ChainableCommander } from "ioredis";
|
||||||
|
|
||||||
export class Cache<T> {
|
export class Cache<T> {
|
||||||
private ttl: number;
|
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 type { ILocalUser } from "@/models/entities/user.js";
|
||||||
import { Users } from "@/models/index.js";
|
import { Users } from "@/models/index.js";
|
||||||
|
|
||||||
export async function fetchProxyAccount(): Promise<ILocalUser | null> {
|
export async function fetchProxyAccount(): Promise<ILocalUser | null> {
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.proxyAccountId == null) return null;
|
if (meta.proxyAccountId == null) return null;
|
||||||
return (await Users.findOneByOrFail({
|
return (await Users.findOneByOrFail({
|
||||||
id: meta.proxyAccountId,
|
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 { Emoji } from "@/models/entities/emoji.js";
|
||||||
import type { Note } from "@/models/entities/note.js";
|
import type { Note } from "@/models/entities/note.js";
|
||||||
import { Cache } from "./cache.js";
|
import { Cache } from "./cache.js";
|
||||||
import { isSelfHost, toPuny } from "backend-rs";
|
import { decodeReaction, isSelfHost, toPuny } from "backend-rs";
|
||||||
import { decodeReaction } from "./reaction-lib.js";
|
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import { query } from "@/prelude/url.js";
|
import { query } from "@/prelude/url.js";
|
||||||
import { redisClient } from "@/db/redis.js";
|
import { redisClient } from "@/db/redis.js";
|
||||||
|
|
|
@ -12,7 +12,7 @@ export function parse(acct: any): Post {
|
||||||
cw: acct.cw,
|
cw: acct.cw,
|
||||||
localOnly: acct.localOnly,
|
localOnly: acct.localOnly,
|
||||||
createdAt: new Date(acct.createdAt),
|
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 { packedEmojiSchema } from "@/models/schema/emoji.js";
|
||||||
import { packedNoteEdit } from "@/models/schema/note-edit.js";
|
import { packedNoteEdit } from "@/models/schema/note-edit.js";
|
||||||
import { packedNoteFileSchema } from "@/models/schema/note-file.js";
|
import { packedNoteFileSchema } from "@/models/schema/note-file.js";
|
||||||
|
import { packedAbuseUserReportSchema } from "@/models/schema/abuse-user-report.js";
|
||||||
|
|
||||||
export const refs = {
|
export const refs = {
|
||||||
|
AbuseUserReport: packedAbuseUserReportSchema,
|
||||||
UserLite: packedUserLiteSchema,
|
UserLite: packedUserLiteSchema,
|
||||||
UserDetailedNotMeOnly: packedUserDetailedNotMeOnlySchema,
|
UserDetailedNotMeOnly: packedUserDetailedNotMeOnlySchema,
|
||||||
MeDetailedOnly: packedMeDetailedOnlySchema,
|
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 { Instance } from "@/models/entities/instance.js";
|
||||||
import type { Meta } from "@/models/entities/meta.js";
|
import type { Meta } from "@/models/entities/meta.js";
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ export async function shouldBlockInstance(
|
||||||
host: Instance["host"],
|
host: Instance["host"],
|
||||||
meta?: Meta,
|
meta?: Meta,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const { blockedHosts } = meta ?? (await fetchMeta());
|
const { blockedHosts } = meta ?? (await fetchMeta(true));
|
||||||
return blockedHosts.some(
|
return blockedHosts.some(
|
||||||
(blockedHost) => host === blockedHost || host.endsWith(`.${blockedHost}`),
|
(blockedHost) => host === blockedHost || host.endsWith(`.${blockedHost}`),
|
||||||
);
|
);
|
||||||
|
@ -30,7 +30,7 @@ export async function shouldSilenceInstance(
|
||||||
host: Instance["host"],
|
host: Instance["host"],
|
||||||
meta?: Meta,
|
meta?: Meta,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const { silencedHosts } = meta ?? (await fetchMeta());
|
const { silencedHosts } = meta ?? (await fetchMeta(true));
|
||||||
return silencedHosts.some(
|
return silencedHosts.some(
|
||||||
(silencedHost) =>
|
(silencedHost) =>
|
||||||
host === silencedHost || host.endsWith(`.${silencedHost}`),
|
host === silencedHost || host.endsWith(`.${silencedHost}`),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Brackets } from "typeorm";
|
import { Brackets } from "typeorm";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "backend-rs";
|
||||||
import { Instances } from "@/models/index.js";
|
import { Instances } from "@/models/index.js";
|
||||||
import type { Instance } from "@/models/entities/instance.js";
|
import type { Instance } from "@/models/entities/instance.js";
|
||||||
import { DAY } from "@/const.js";
|
import { DAY } from "@/const.js";
|
||||||
|
@ -19,7 +19,7 @@ export async function skippedInstances(
|
||||||
hosts: Instance["host"][],
|
hosts: Instance["host"][],
|
||||||
): Promise<Instance["host"][]> {
|
): Promise<Instance["host"][]> {
|
||||||
// first check for blocked instances since that info may already be in memory
|
// 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(
|
const shouldSkip = await Promise.all(
|
||||||
hosts.map((host) => shouldBlockInstance(host, meta)),
|
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 fetch from "node-fetch";
|
||||||
import { Converter } from "opencc-js";
|
import { Converter } from "opencc-js";
|
||||||
import { getAgentByUrl } from "@/misc/fetch.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 type { PostLanguage } from "@/misc/langmap";
|
||||||
import * as deepl from "deepl-node";
|
import * as deepl from "deepl-node";
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ export async function translate(
|
||||||
from: PostLanguage | null,
|
from: PostLanguage | null,
|
||||||
to: PostLanguage,
|
to: PostLanguage,
|
||||||
) {
|
) {
|
||||||
const instance = await fetchMeta();
|
const instance = await fetchMeta(true);
|
||||||
|
|
||||||
if (instance.deeplAuthKey == null && instance.libreTranslateApiUrl == null) {
|
if (instance.deeplAuthKey == null && instance.libreTranslateApiUrl == null) {
|
||||||
throw Error("No translator is set up on this server.");
|
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 { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js";
|
||||||
import { NoteFile } from "./note-file.js";
|
import { NoteFile } from "./note-file.js";
|
||||||
|
|
||||||
|
export type DriveFileUsageHint = "userAvatar" | "userBanner" | null;
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@Index(["userId", "folderId", "id"])
|
@Index(["userId", "folderId", "id"])
|
||||||
export class DriveFile {
|
export class DriveFile {
|
||||||
|
@ -177,6 +179,14 @@ export class DriveFile {
|
||||||
})
|
})
|
||||||
public isSensitive: boolean;
|
public isSensitive: boolean;
|
||||||
|
|
||||||
|
// Hint for what this file is used for
|
||||||
|
@Column({
|
||||||
|
type: "enum",
|
||||||
|
enum: ["userAvatar", "userBanner"],
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public usageHint: DriveFileUsageHint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 外部の(信頼されていない)URLへの直リンクか否か
|
* 外部の(信頼されていない)URLへの直リンクか否か
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -276,6 +276,12 @@ export class Meta {
|
||||||
})
|
})
|
||||||
public remoteDriveCapacityMb: number;
|
public remoteDriveCapacityMb: number;
|
||||||
|
|
||||||
|
@Column("integer", {
|
||||||
|
default: 5,
|
||||||
|
comment: "Antenna Limit",
|
||||||
|
})
|
||||||
|
public antennaLimit: number;
|
||||||
|
|
||||||
@Column("varchar", {
|
@Column("varchar", {
|
||||||
length: 128,
|
length: 128,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
|
|
@ -72,9 +72,8 @@ export class Note {
|
||||||
})
|
})
|
||||||
public name: string | null;
|
public name: string | null;
|
||||||
|
|
||||||
@Index() // USING pgroonga pgroonga_varchar_full_text_search_ops_v2
|
@Index() // USING pgroonga
|
||||||
@Column("varchar", {
|
@Column("text", {
|
||||||
length: 512,
|
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
public cw: string | null;
|
public cw: string | null;
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { db } from "@/db/postgre.js";
|
||||||
import { Users } from "../index.js";
|
import { Users } from "../index.js";
|
||||||
import { AbuseUserReport } from "@/models/entities/abuse-user-report.js";
|
import { AbuseUserReport } from "@/models/entities/abuse-user-report.js";
|
||||||
import { awaitAll } from "@/prelude/await-all.js";
|
import { awaitAll } from "@/prelude/await-all.js";
|
||||||
|
import type { Packed } from "@/misc/schema.js";
|
||||||
|
|
||||||
export const AbuseUserReportRepository = db
|
export const AbuseUserReportRepository = db
|
||||||
.getRepository(AbuseUserReport)
|
.getRepository(AbuseUserReport)
|
||||||
|
@ -10,7 +11,7 @@ export const AbuseUserReportRepository = db
|
||||||
const report =
|
const report =
|
||||||
typeof src === "object" ? src : await this.findOneByOrFail({ id: src });
|
typeof src === "object" ? src : await this.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
return await awaitAll({
|
const packed: Packed<"AbuseUserReport"> = await awaitAll({
|
||||||
id: report.id,
|
id: report.id,
|
||||||
createdAt: report.createdAt.toISOString(),
|
createdAt: report.createdAt.toISOString(),
|
||||||
comment: report.comment,
|
comment: report.comment,
|
||||||
|
@ -31,9 +32,10 @@ export const AbuseUserReportRepository = db
|
||||||
: null,
|
: null,
|
||||||
forwarded: report.forwarded,
|
forwarded: report.forwarded,
|
||||||
});
|
});
|
||||||
|
return packed;
|
||||||
},
|
},
|
||||||
|
|
||||||
packMany(reports: any[]) {
|
packMany(reports: (AbuseUserReport["id"] | AbuseUserReport)[]) {
|
||||||
return Promise.all(reports.map((x) => this.pack(x)));
|
return Promise.all(reports.map((x) => this.pack(x)));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -40,6 +40,7 @@ export const ChannelRepository = db.getRepository(Channel).extend({
|
||||||
name: channel.name,
|
name: channel.name,
|
||||||
description: channel.description,
|
description: channel.description,
|
||||||
userId: channel.userId,
|
userId: channel.userId,
|
||||||
|
bannerId: channel.bannerId,
|
||||||
bannerUrl: banner ? DriveFiles.getPublicUrl(banner, false) : null,
|
bannerUrl: banner ? DriveFiles.getPublicUrl(banner, false) : null,
|
||||||
usersCount: channel.usersCount,
|
usersCount: channel.usersCount,
|
||||||
notesCount: channel.notesCount,
|
notesCount: channel.notesCount,
|
||||||
|
|
|
@ -152,6 +152,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
|
||||||
md5: file.md5,
|
md5: file.md5,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
isSensitive: file.isSensitive,
|
isSensitive: file.isSensitive,
|
||||||
|
usageHint: file.usageHint,
|
||||||
blurhash: file.blurhash,
|
blurhash: file.blurhash,
|
||||||
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
||||||
url: opts.self ? file.url : this.getPublicUrl(file, false),
|
url: opts.self ? file.url : this.getPublicUrl(file, false),
|
||||||
|
@ -193,6 +194,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
|
||||||
md5: file.md5,
|
md5: file.md5,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
isSensitive: file.isSensitive,
|
isSensitive: file.isSensitive,
|
||||||
|
usageHint: file.usageHint,
|
||||||
blurhash: file.blurhash,
|
blurhash: file.blurhash,
|
||||||
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
||||||
url: opts.self ? file.url : this.getPublicUrl(file, false),
|
url: opts.self ? file.url : this.getPublicUrl(file, false),
|
||||||
|
|
|
@ -19,7 +19,9 @@ export const GalleryPostRepository = db.getRepository(GalleryPost).extend({
|
||||||
createdAt: post.createdAt.toISOString(),
|
createdAt: post.createdAt.toISOString(),
|
||||||
updatedAt: post.updatedAt.toISOString(),
|
updatedAt: post.updatedAt.toISOString(),
|
||||||
userId: post.userId,
|
userId: post.userId,
|
||||||
user: Users.pack(post.user || post.userId, me),
|
user: Users.pack(post.user || post.userId, me, {
|
||||||
|
detail: true,
|
||||||
|
}),
|
||||||
title: post.title,
|
title: post.title,
|
||||||
description: post.description,
|
description: post.description,
|
||||||
fileIds: post.fileIds,
|
fileIds: post.fileIds,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { db } from "@/db/postgre.js";
|
||||||
import { NoteReaction } from "@/models/entities/note-reaction.js";
|
import { NoteReaction } from "@/models/entities/note-reaction.js";
|
||||||
import { Notes, Users } from "../index.js";
|
import { Notes, Users } from "../index.js";
|
||||||
import type { Packed } from "@/misc/schema.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";
|
import type { User } from "@/models/entities/user.js";
|
||||||
|
|
||||||
export const NoteReactionRepository = db.getRepository(NoteReaction).extend({
|
export const NoteReactionRepository = db.getRepository(NoteReaction).extend({
|
||||||
|
|
|
@ -12,9 +12,8 @@ import {
|
||||||
Channels,
|
Channels,
|
||||||
} from "../index.js";
|
} from "../index.js";
|
||||||
import type { Packed } from "@/misc/schema.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 { awaitAll } from "@/prelude/await-all.js";
|
||||||
import { convertReactions, decodeReaction } from "@/misc/reaction-lib.js";
|
|
||||||
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
||||||
import {
|
import {
|
||||||
aggregateNoteEmojis,
|
aggregateNoteEmojis,
|
||||||
|
@ -214,7 +213,7 @@ export const NoteRepository = db.getRepository(Note).extend({
|
||||||
note.visibility === "specified" ? note.visibleUserIds : undefined,
|
note.visibility === "specified" ? note.visibleUserIds : undefined,
|
||||||
renoteCount: note.renoteCount,
|
renoteCount: note.renoteCount,
|
||||||
repliesCount: note.repliesCount,
|
repliesCount: note.repliesCount,
|
||||||
reactions: convertReactions(note.reactions),
|
reactions: countReactions(note.reactions),
|
||||||
reactionEmojis: reactionEmoji,
|
reactionEmojis: reactionEmoji,
|
||||||
emojis: noteEmoji,
|
emojis: noteEmoji,
|
||||||
tags: note.tags.length > 0 ? note.tags : undefined,
|
tags: note.tags.length > 0 ? note.tags : undefined,
|
||||||
|
@ -233,6 +232,7 @@ export const NoteRepository = db.getRepository(Note).extend({
|
||||||
uri: note.uri || undefined,
|
uri: note.uri || undefined,
|
||||||
url: note.url || undefined,
|
url: note.url || undefined,
|
||||||
updatedAt: note.updatedAt?.toISOString() || undefined,
|
updatedAt: note.updatedAt?.toISOString() || undefined,
|
||||||
|
hasPoll: note.hasPoll,
|
||||||
poll: note.hasPoll ? populatePoll(note, meId) : undefined,
|
poll: note.hasPoll ? populatePoll(note, meId) : undefined,
|
||||||
...(meId
|
...(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,
|
nullable: true,
|
||||||
optional: false,
|
optional: false,
|
||||||
},
|
},
|
||||||
|
bannerId: {
|
||||||
|
type: "string",
|
||||||
|
optional: false,
|
||||||
|
nullable: true,
|
||||||
|
format: "id",
|
||||||
|
example: "xxxxxxxxxx",
|
||||||
|
},
|
||||||
notesCount: {
|
notesCount: {
|
||||||
type: "number",
|
type: "number",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
@ -57,5 +64,10 @@ export const packedChannelSchema = {
|
||||||
optional: false,
|
optional: false,
|
||||||
format: "id",
|
format: "id",
|
||||||
},
|
},
|
||||||
|
hasUnreadNote: {
|
||||||
|
type: "boolean",
|
||||||
|
optional: true,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -44,6 +44,12 @@ export const packedDriveFileSchema = {
|
||||||
optional: false,
|
optional: false,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
|
usageHint: {
|
||||||
|
type: "string",
|
||||||
|
optional: false,
|
||||||
|
nullable: true,
|
||||||
|
enum: ["userAvatar", "userBanner"],
|
||||||
|
},
|
||||||
blurhash: {
|
blurhash: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
|
|
|
@ -38,7 +38,7 @@ export const packedGalleryPostSchema = {
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
type: "object",
|
type: "object",
|
||||||
ref: "UserLite",
|
ref: "UserDetailed",
|
||||||
optional: false,
|
optional: false,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
|
@ -79,5 +79,15 @@ export const packedGalleryPostSchema = {
|
||||||
optional: false,
|
optional: false,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
|
isLiked: {
|
||||||
|
type: "boolean",
|
||||||
|
optional: true,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
likedCount: {
|
||||||
|
type: "number",
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -28,7 +28,7 @@ export const packedNoteSchema = {
|
||||||
},
|
},
|
||||||
cw: {
|
cw: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: true,
|
optional: false,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
},
|
},
|
||||||
userId: {
|
userId: {
|
||||||
|
@ -98,7 +98,7 @@ export const packedNoteSchema = {
|
||||||
},
|
},
|
||||||
fileIds: {
|
fileIds: {
|
||||||
type: "array",
|
type: "array",
|
||||||
optional: true,
|
optional: false,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
items: {
|
items: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
@ -128,6 +128,11 @@ export const packedNoteSchema = {
|
||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
hasPoll: {
|
||||||
|
type: "boolean",
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
poll: {
|
poll: {
|
||||||
type: "object",
|
type: "object",
|
||||||
optional: true,
|
optional: true,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import config from "@/config/index.js";
|
||||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||||
import type { IActivity } from "@/remote/activitypub/type.js";
|
import type { IActivity } from "@/remote/activitypub/type.js";
|
||||||
import type { Webhook, webhookEventTypes } from "@/models/entities/webhook.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 processDeliver from "./processors/deliver.js";
|
||||||
import processInbox from "./processors/inbox.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) {
|
function apBackoff(attemptsMade: number, err: Error) {
|
||||||
const baseDelay = 60 * 1000; // 1min
|
const baseDelay = 60 * 1000; // 1min
|
||||||
const maxBackoff = 8 * 60 * 60 * 1000; // 8hours
|
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.min(backoff, maxBackoff);
|
||||||
backoff += Math.round(backoff * Math.random() * 0.2);
|
backoff += Math.round(backoff * Math.random() * 0.2);
|
||||||
return backoff;
|
return backoff;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import perform from "@/remote/activitypub/perform.js";
|
||||||
import Logger from "@/services/logger.js";
|
import Logger from "@/services/logger.js";
|
||||||
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
|
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
|
||||||
import { Instances } from "@/models/index.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 { toPuny, extractHost } from "backend-rs";
|
||||||
import { getApId } from "@/remote/activitypub/type.js";
|
import { getApId } from "@/remote/activitypub/type.js";
|
||||||
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.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);
|
const host = toPuny(new URL(signature.keyId).hostname);
|
||||||
|
|
||||||
// interrupt if blocked
|
// interrupt if blocked
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (await shouldBlockInstance(host, meta)) {
|
if (await shouldBlockInstance(host, meta)) {
|
||||||
return `Blocked request: ${host}`;
|
return `Blocked request: ${host}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { URL } from "url";
|
import { URL } from "url";
|
||||||
import httpSignature, { IParsedSignature } from "@peertube/http-signature";
|
import httpSignature, { IParsedSignature } from "@peertube/http-signature";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "backend-rs";
|
||||||
import { toPuny } from "backend-rs";
|
import { toPuny } from "backend-rs";
|
||||||
import DbResolver from "@/remote/activitypub/db-resolver.js";
|
import DbResolver from "@/remote/activitypub/db-resolver.js";
|
||||||
import { getApId } from "@/remote/activitypub/type.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";
|
import { verify } from "node:crypto";
|
||||||
|
|
||||||
export async function hasSignature(req: IncomingMessage): Promise<string> {
|
export async function hasSignature(req: IncomingMessage): Promise<string> {
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
const required = meta.secureMode || meta.privateMode;
|
const required = meta.secureMode || meta.privateMode;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -27,7 +27,7 @@ export async function hasSignature(req: IncomingMessage): Promise<string> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkFetch(req: IncomingMessage): Promise<number> {
|
export async function checkFetch(req: IncomingMessage): Promise<number> {
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
if (req.headers.host !== config.host) return 400;
|
if (req.headers.host !== config.host) return 400;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { CacheableRemoteUser } from "@/models/entities/user.js";
|
import type { CacheableRemoteUser } from "@/models/entities/user.js";
|
||||||
import deleteNode from "@/services/note/delete.js";
|
import deleteNote from "@/services/note/delete.js";
|
||||||
import { apLogger } from "../../logger.js";
|
import { apLogger } from "../../logger.js";
|
||||||
import DbResolver from "../../db-resolver.js";
|
import DbResolver from "../../db-resolver.js";
|
||||||
import { getApLock } from "@/misc/app-lock.js";
|
import { getApLock } from "@/misc/app-lock.js";
|
||||||
|
@ -36,7 +36,7 @@ export default async function (
|
||||||
return "The user trying to delete the post is not the post author";
|
return "The user trying to delete the post is not the post author";
|
||||||
}
|
}
|
||||||
|
|
||||||
await deleteNode(actor, note);
|
await deleteNote(actor, note);
|
||||||
return "ok: note deleted";
|
return "ok: note deleted";
|
||||||
} finally {
|
} finally {
|
||||||
await lock.release();
|
await lock.release();
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import { uploadFromUrl } from "@/services/drive/upload-from-url.js";
|
import { uploadFromUrl } from "@/services/drive/upload-from-url.js";
|
||||||
import type { CacheableRemoteUser } from "@/models/entities/user.js";
|
import type { CacheableRemoteUser } from "@/models/entities/user.js";
|
||||||
import Resolver from "../resolver.js";
|
import Resolver from "../resolver.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "backend-rs";
|
||||||
import { apLogger } from "../logger.js";
|
import { apLogger } from "../logger.js";
|
||||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
import type {
|
||||||
|
DriveFile,
|
||||||
|
DriveFileUsageHint,
|
||||||
|
} from "@/models/entities/drive-file.js";
|
||||||
import { DriveFiles } from "@/models/index.js";
|
import { DriveFiles } from "@/models/index.js";
|
||||||
import { truncate } from "@/misc/truncate.js";
|
import { truncate } from "@/misc/truncate.js";
|
||||||
import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js";
|
import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js";
|
||||||
|
@ -16,6 +19,7 @@ const logger = apLogger;
|
||||||
export async function createImage(
|
export async function createImage(
|
||||||
actor: CacheableRemoteUser,
|
actor: CacheableRemoteUser,
|
||||||
value: any,
|
value: any,
|
||||||
|
usage: DriveFileUsageHint,
|
||||||
): Promise<DriveFile> {
|
): Promise<DriveFile> {
|
||||||
// Skip if author is frozen.
|
// Skip if author is frozen.
|
||||||
if (actor.isSuspended) {
|
if (actor.isSuspended) {
|
||||||
|
@ -34,7 +38,7 @@ export async function createImage(
|
||||||
|
|
||||||
logger.info(`Creating the Image: ${image.url}`);
|
logger.info(`Creating the Image: ${image.url}`);
|
||||||
|
|
||||||
const instance = await fetchMeta();
|
const instance = await fetchMeta(true);
|
||||||
|
|
||||||
let file = await uploadFromUrl({
|
let file = await uploadFromUrl({
|
||||||
url: image.url,
|
url: image.url,
|
||||||
|
@ -43,6 +47,7 @@ export async function createImage(
|
||||||
sensitive: image.sensitive,
|
sensitive: image.sensitive,
|
||||||
isLink: !instance.cacheRemoteFiles,
|
isLink: !instance.cacheRemoteFiles,
|
||||||
comment: truncate(image.name, DB_MAX_IMAGE_COMMENT_LENGTH),
|
comment: truncate(image.name, DB_MAX_IMAGE_COMMENT_LENGTH),
|
||||||
|
usageHint: usage,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (file.isLink) {
|
if (file.isLink) {
|
||||||
|
@ -73,9 +78,10 @@ export async function createImage(
|
||||||
export async function resolveImage(
|
export async function resolveImage(
|
||||||
actor: CacheableRemoteUser,
|
actor: CacheableRemoteUser,
|
||||||
value: any,
|
value: any,
|
||||||
|
usage: DriveFileUsageHint,
|
||||||
): Promise<DriveFile> {
|
): Promise<DriveFile> {
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
// Fetch from remote server and register
|
// Fetch from remote server and register
|
||||||
return await createImage(actor, value);
|
return await createImage(actor, value, usage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,7 +213,8 @@ export async function createNote(
|
||||||
? (
|
? (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
note.attachment.map(
|
note.attachment.map(
|
||||||
(x) => limit(() => resolveImage(actor, x)) as Promise<DriveFile>,
|
(x) =>
|
||||||
|
limit(() => resolveImage(actor, x, null)) as Promise<DriveFile>,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
).filter((image) => image != null)
|
).filter((image) => image != null)
|
||||||
|
@ -616,7 +617,7 @@ export async function updateNote(value: string | IObject, resolver?: Resolver) {
|
||||||
fileList.map(
|
fileList.map(
|
||||||
(x) =>
|
(x) =>
|
||||||
limit(async () => {
|
limit(async () => {
|
||||||
const file = await resolveImage(actor, x);
|
const file = await resolveImage(actor, x, null);
|
||||||
const update: Partial<DriveFile> = {};
|
const update: Partial<DriveFile> = {};
|
||||||
|
|
||||||
const altText = truncate(x.name, DB_MAX_IMAGE_COMMENT_LENGTH);
|
const altText = truncate(x.name, DB_MAX_IMAGE_COMMENT_LENGTH);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
Followings,
|
Followings,
|
||||||
UserProfiles,
|
UserProfiles,
|
||||||
UserPublickeys,
|
UserPublickeys,
|
||||||
|
DriveFiles,
|
||||||
} from "@/models/index.js";
|
} from "@/models/index.js";
|
||||||
import type { IRemoteUser, CacheableUser } from "@/models/entities/user.js";
|
import type { IRemoteUser, CacheableUser } from "@/models/entities/user.js";
|
||||||
import { User } from "@/models/entities/user.js";
|
import { User } from "@/models/entities/user.js";
|
||||||
|
@ -362,10 +363,14 @@ export async function createPerson(
|
||||||
|
|
||||||
//#region Fetch avatar and header image
|
//#region Fetch avatar and header image
|
||||||
const [avatar, banner] = await Promise.all(
|
const [avatar, banner] = await Promise.all(
|
||||||
[person.icon, person.image].map((img) =>
|
[person.icon, person.image].map((img, index) =>
|
||||||
img == null
|
img == null
|
||||||
? Promise.resolve(null)
|
? Promise.resolve(null)
|
||||||
: resolveImage(user!, img).catch(() => null),
|
: resolveImage(
|
||||||
|
user,
|
||||||
|
img,
|
||||||
|
index === 0 ? "userAvatar" : index === 1 ? "userBanner" : null,
|
||||||
|
).catch(() => null),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -438,10 +443,14 @@ export async function updatePerson(
|
||||||
|
|
||||||
// Fetch avatar and header image
|
// Fetch avatar and header image
|
||||||
const [avatar, banner] = await Promise.all(
|
const [avatar, banner] = await Promise.all(
|
||||||
[person.icon, person.image].map((img) =>
|
[person.icon, person.image].map((img, index) =>
|
||||||
img == null
|
img == null
|
||||||
? Promise.resolve(null)
|
? Promise.resolve(null)
|
||||||
: resolveImage(user, img).catch(() => null),
|
: resolveImage(
|
||||||
|
user,
|
||||||
|
img,
|
||||||
|
index === 0 ? "userAvatar" : index === 1 ? "userBanner" : null,
|
||||||
|
).catch(() => null),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -561,10 +570,14 @@ export async function updatePerson(
|
||||||
} as Partial<User>;
|
} as Partial<User>;
|
||||||
|
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
|
if (user?.avatarId)
|
||||||
|
await DriveFiles.update(user.avatarId, { usageHint: null });
|
||||||
updates.avatarId = avatar.id;
|
updates.avatarId = avatar.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (banner) {
|
if (banner) {
|
||||||
|
if (user?.bannerId)
|
||||||
|
await DriveFiles.update(user.bannerId, { usageHint: null });
|
||||||
updates.bannerId = banner.id;
|
updates.bannerId = banner.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import type { ILocalUser } from "@/models/entities/user.js";
|
import type { ILocalUser } from "@/models/entities/user.js";
|
||||||
import { getInstanceActor } from "@/services/instance-actor.js";
|
import { getInstanceActor } from "@/services/instance-actor.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "backend-rs";
|
||||||
import { extractHost, isSelfHost } from "backend-rs";
|
import { extractHost, isSelfHost } from "backend-rs";
|
||||||
import { apGet } from "./request.js";
|
import { apGet } from "./request.js";
|
||||||
import type { IObject, ICollection, IOrderedCollection } from "./type.js";
|
import type { IObject, ICollection, IOrderedCollection } from "./type.js";
|
||||||
|
@ -100,7 +100,7 @@ export default class Resolver {
|
||||||
return await this.resolveLocal(value);
|
return await this.resolveLocal(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (await shouldBlockInstance(host, meta)) {
|
if (await shouldBlockInstance(host, meta)) {
|
||||||
throw new Error("Instance is blocked");
|
throw new Error("Instance is blocked");
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import renderKey from "@/remote/activitypub/renderer/key.js";
|
||||||
import { renderPerson } from "@/remote/activitypub/renderer/person.js";
|
import { renderPerson } from "@/remote/activitypub/renderer/person.js";
|
||||||
import renderEmoji from "@/remote/activitypub/renderer/emoji.js";
|
import renderEmoji from "@/remote/activitypub/renderer/emoji.js";
|
||||||
import { inbox as processInbox } from "@/queue/index.js";
|
import { inbox as processInbox } from "@/queue/index.js";
|
||||||
import { isSelfHost } from "backend-rs";
|
import { fetchMeta, isSelfHost } from "backend-rs";
|
||||||
import {
|
import {
|
||||||
Notes,
|
Notes,
|
||||||
Users,
|
Users,
|
||||||
|
@ -25,7 +25,6 @@ import {
|
||||||
getSignatureUser,
|
getSignatureUser,
|
||||||
} from "@/remote/activitypub/check-fetch.js";
|
} from "@/remote/activitypub/check-fetch.js";
|
||||||
import { getInstanceActor } from "@/services/instance-actor.js";
|
import { getInstanceActor } from "@/services/instance-actor.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
|
||||||
import renderFollow from "@/remote/activitypub/renderer/follow.js";
|
import renderFollow from "@/remote/activitypub/renderer/follow.js";
|
||||||
import Featured from "./activitypub/featured.js";
|
import Featured from "./activitypub/featured.js";
|
||||||
import Following from "./activitypub/following.js";
|
import Following from "./activitypub/following.js";
|
||||||
|
@ -33,7 +32,7 @@ import Followers from "./activitypub/followers.js";
|
||||||
import Outbox, { packActivity } from "./activitypub/outbox.js";
|
import Outbox, { packActivity } from "./activitypub/outbox.js";
|
||||||
import { serverLogger } from "./index.js";
|
import { serverLogger } from "./index.js";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import Koa from "koa";
|
import type Koa from "koa";
|
||||||
import * as crypto from "node:crypto";
|
import * as crypto from "node:crypto";
|
||||||
import { inspect } from "node:util";
|
import { inspect } from "node:util";
|
||||||
import type { IActivity } from "@/remote/activitypub/type.js";
|
import type { IActivity } from "@/remote/activitypub/type.js";
|
||||||
|
@ -238,7 +237,7 @@ router.get("/notes/:note", async (ctx, next) => {
|
||||||
|
|
||||||
ctx.body = renderActivity(await renderNote(note, false));
|
ctx.body = renderActivity(await renderNote(note, false));
|
||||||
|
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||||
} else {
|
} else {
|
||||||
|
@ -268,7 +267,7 @@ router.get("/notes/:note/activity", async (ctx) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = renderActivity(await packActivity(note));
|
ctx.body = renderActivity(await packActivity(note));
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||||
} else {
|
} else {
|
||||||
|
@ -323,7 +322,7 @@ router.get("/users/:user/publickey", async (ctx) => {
|
||||||
|
|
||||||
if (Users.isLocalUser(user)) {
|
if (Users.isLocalUser(user)) {
|
||||||
ctx.body = renderActivity(renderKey(user, keypair));
|
ctx.body = renderActivity(renderKey(user, keypair));
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||||
} else {
|
} else {
|
||||||
|
@ -343,7 +342,7 @@ async function userInfo(ctx: Router.RouterContext, user: User | null) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = renderActivity(await renderPerson(user as ILocalUser));
|
ctx.body = renderActivity(await renderPerson(user as ILocalUser));
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||||
} else {
|
} else {
|
||||||
|
@ -426,8 +425,8 @@ router.get("/emojis/:emoji", async (ctx) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = renderActivity(await renderEmoji(emoji));
|
ctx.body = renderActivity(renderEmoji(emoji));
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||||
} else {
|
} else {
|
||||||
|
@ -459,7 +458,7 @@ router.get("/likes/:like", async (ctx) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = renderActivity(await renderLike(reaction, note));
|
ctx.body = renderActivity(await renderLike(reaction, note));
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||||
} else {
|
} else {
|
||||||
|
@ -497,7 +496,7 @@ router.get(
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = renderActivity(renderFollow(follower, followee));
|
ctx.body = renderActivity(renderFollow(follower, followee));
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||||
} else {
|
} else {
|
||||||
|
@ -540,7 +539,7 @@ router.get("/follows/:followRequestId", async (ctx: Router.RouterContext) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import renderOrderedCollection from "@/remote/activitypub/renderer/ordered-colle
|
||||||
import renderNote from "@/remote/activitypub/renderer/note.js";
|
import renderNote from "@/remote/activitypub/renderer/note.js";
|
||||||
import { Users, Notes, UserNotePinings } from "@/models/index.js";
|
import { Users, Notes, UserNotePinings } from "@/models/index.js";
|
||||||
import { checkFetch } from "@/remote/activitypub/check-fetch.js";
|
import { checkFetch } from "@/remote/activitypub/check-fetch.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "backend-rs";
|
||||||
import { setResponseType } from "../activitypub.js";
|
import { setResponseType } from "../activitypub.js";
|
||||||
import type Router from "@koa/router";
|
import type Router from "@koa/router";
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ export default async (ctx: Router.RouterContext) => {
|
||||||
|
|
||||||
ctx.body = renderActivity(rendered);
|
ctx.body = renderActivity(rendered);
|
||||||
|
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import renderFollowUser from "@/remote/activitypub/renderer/follow-user.js";
|
||||||
import { Users, Followings, UserProfiles } from "@/models/index.js";
|
import { Users, Followings, UserProfiles } from "@/models/index.js";
|
||||||
import type { Following } from "@/models/entities/following.js";
|
import type { Following } from "@/models/entities/following.js";
|
||||||
import { checkFetch } from "@/remote/activitypub/check-fetch.js";
|
import { checkFetch } from "@/remote/activitypub/check-fetch.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "backend-rs";
|
||||||
import { setResponseType } from "../activitypub.js";
|
import { setResponseType } from "../activitypub.js";
|
||||||
import type { FindOptionsWhere } from "typeorm";
|
import type { FindOptionsWhere } from "typeorm";
|
||||||
import type Router from "@koa/router";
|
import type Router from "@koa/router";
|
||||||
|
@ -110,7 +110,7 @@ export default async (ctx: Router.RouterContext) => {
|
||||||
ctx.body = renderActivity(rendered);
|
ctx.body = renderActivity(rendered);
|
||||||
setResponseType(ctx);
|
setResponseType(ctx);
|
||||||
}
|
}
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import renderFollowUser from "@/remote/activitypub/renderer/follow-user.js";
|
||||||
import { Users, Followings, UserProfiles } from "@/models/index.js";
|
import { Users, Followings, UserProfiles } from "@/models/index.js";
|
||||||
import type { Following } from "@/models/entities/following.js";
|
import type { Following } from "@/models/entities/following.js";
|
||||||
import { checkFetch } from "@/remote/activitypub/check-fetch.js";
|
import { checkFetch } from "@/remote/activitypub/check-fetch.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "backend-rs";
|
||||||
import { setResponseType } from "../activitypub.js";
|
import { setResponseType } from "../activitypub.js";
|
||||||
import type { FindOptionsWhere } from "typeorm";
|
import type { FindOptionsWhere } from "typeorm";
|
||||||
import type Router from "@koa/router";
|
import type Router from "@koa/router";
|
||||||
|
@ -110,7 +110,7 @@ export default async (ctx: Router.RouterContext) => {
|
||||||
ctx.body = renderActivity(rendered);
|
ctx.body = renderActivity(rendered);
|
||||||
setResponseType(ctx);
|
setResponseType(ctx);
|
||||||
}
|
}
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import * as url from "@/prelude/url.js";
|
||||||
import { Users, Notes } from "@/models/index.js";
|
import { Users, Notes } from "@/models/index.js";
|
||||||
import type { Note } from "@/models/entities/note.js";
|
import type { Note } from "@/models/entities/note.js";
|
||||||
import { checkFetch } from "@/remote/activitypub/check-fetch.js";
|
import { checkFetch } from "@/remote/activitypub/check-fetch.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "backend-rs";
|
||||||
import { makePaginationQuery } from "../api/common/make-pagination-query.js";
|
import { makePaginationQuery } from "../api/common/make-pagination-query.js";
|
||||||
import { setResponseType } from "../activitypub.js";
|
import { setResponseType } from "../activitypub.js";
|
||||||
import type Router from "@koa/router";
|
import type Router from "@koa/router";
|
||||||
|
@ -117,7 +117,7 @@ export default async (ctx: Router.RouterContext) => {
|
||||||
|
|
||||||
setResponseType(ctx);
|
setResponseType(ctx);
|
||||||
}
|
}
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import type Koa from "koa";
|
||||||
|
|
||||||
import type { User } from "@/models/entities/user.js";
|
import type { User } from "@/models/entities/user.js";
|
||||||
import { UserIps } from "@/models/index.js";
|
import { UserIps } from "@/models/index.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "backend-rs";
|
||||||
import type { IEndpoint } from "./endpoints.js";
|
import type { IEndpoint } from "./endpoints.js";
|
||||||
import authenticate, { AuthenticationError } from "./authenticate.js";
|
import authenticate, { AuthenticationError } from "./authenticate.js";
|
||||||
import call from "./call.js";
|
import call from "./call.js";
|
||||||
|
@ -84,7 +84,7 @@ export default (endpoint: IEndpoint, ctx: Koa.Context) =>
|
||||||
|
|
||||||
// Log IP
|
// Log IP
|
||||||
if (user) {
|
if (user) {
|
||||||
fetchMeta().then((meta) => {
|
fetchMeta(true).then((meta) => {
|
||||||
if (!meta.enableIpLogging) return;
|
if (!meta.enableIpLogging) return;
|
||||||
const ip = ctx.ip;
|
const ip = ctx.ip;
|
||||||
const ips = userIpHistories.get(user.id);
|
const ips = userIpHistories.get(user.id);
|
||||||
|
|
|
@ -10,7 +10,7 @@ import endpoints from "./endpoints.js";
|
||||||
import compatibility from "./compatibility.js";
|
import compatibility from "./compatibility.js";
|
||||||
import { ApiError } from "./error.js";
|
import { ApiError } from "./error.js";
|
||||||
import { apiLogger } from "./logger.js";
|
import { apiLogger } from "./logger.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "backend-rs";
|
||||||
|
|
||||||
const accessDenied = {
|
const accessDenied = {
|
||||||
message: "Access denied.",
|
message: "Access denied.",
|
||||||
|
@ -117,7 +117,7 @@ export default async (
|
||||||
}
|
}
|
||||||
|
|
||||||
// private mode
|
// private mode
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta(true);
|
||||||
if (
|
if (
|
||||||
meta.privateMode &&
|
meta.privateMode &&
|
||||||
ep.meta.requireCredentialPrivateMode &&
|
ep.meta.requireCredentialPrivateMode &&
|
||||||
|
|
|
@ -4,12 +4,11 @@ import { User } from "@/models/entities/user.js";
|
||||||
import { Users, UsedUsernames } from "@/models/index.js";
|
import { Users, UsedUsernames } from "@/models/index.js";
|
||||||
import { UserProfile } from "@/models/entities/user-profile.js";
|
import { UserProfile } from "@/models/entities/user-profile.js";
|
||||||
import { IsNull } from "typeorm";
|
import { IsNull } from "typeorm";
|
||||||
import { genId, toPuny } from "backend-rs";
|
import { genId, hashPassword, toPuny } from "backend-rs";
|
||||||
import { UserKeypair } from "@/models/entities/user-keypair.js";
|
import { UserKeypair } from "@/models/entities/user-keypair.js";
|
||||||
import { UsedUsername } from "@/models/entities/used-username.js";
|
import { UsedUsername } from "@/models/entities/used-username.js";
|
||||||
import { db } from "@/db/postgre.js";
|
import { db } from "@/db/postgre.js";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import { hashPassword } from "@/misc/password.js";
|
|
||||||
|
|
||||||
export async function signup(opts: {
|
export async function signup(opts: {
|
||||||
username: User["username"];
|
username: User["username"];
|
||||||
|
@ -40,7 +39,7 @@ export async function signup(opts: {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate hash of password
|
// Generate hash of password
|
||||||
hash = await hashPassword(password);
|
hash = hashPassword(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate secret
|
// Generate secret
|
||||||
|
|
|
@ -16,68 +16,7 @@ export const meta = {
|
||||||
type: "object",
|
type: "object",
|
||||||
optional: false,
|
optional: false,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
properties: {
|
ref: "AbuseUserReport",
|
||||||
id: {
|
|
||||||
type: "string",
|
|
||||||
nullable: false,
|
|
||||||
optional: false,
|
|
||||||
format: "id",
|
|
||||||
example: "xxxxxxxxxx",
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
type: "string",
|
|
||||||
nullable: false,
|
|
||||||
optional: false,
|
|
||||||
format: "date-time",
|
|
||||||
},
|
|
||||||
comment: {
|
|
||||||
type: "string",
|
|
||||||
nullable: false,
|
|
||||||
optional: false,
|
|
||||||
},
|
|
||||||
resolved: {
|
|
||||||
type: "boolean",
|
|
||||||
nullable: false,
|
|
||||||
optional: false,
|
|
||||||
example: false,
|
|
||||||
},
|
|
||||||
reporterId: {
|
|
||||||
type: "string",
|
|
||||||
nullable: false,
|
|
||||||
optional: false,
|
|
||||||
format: "id",
|
|
||||||
},
|
|
||||||
targetUserId: {
|
|
||||||
type: "string",
|
|
||||||
nullable: false,
|
|
||||||
optional: false,
|
|
||||||
format: "id",
|
|
||||||
},
|
|
||||||
assigneeId: {
|
|
||||||
type: "string",
|
|
||||||
nullable: true,
|
|
||||||
optional: false,
|
|
||||||
format: "id",
|
|
||||||
},
|
|
||||||
reporter: {
|
|
||||||
type: "object",
|
|
||||||
nullable: false,
|
|
||||||
optional: false,
|
|
||||||
ref: "User",
|
|
||||||
},
|
|
||||||
targetUser: {
|
|
||||||
type: "object",
|
|
||||||
nullable: false,
|
|
||||||
optional: false,
|
|
||||||
ref: "User",
|
|
||||||
},
|
|
||||||
assignee: {
|
|
||||||
type: "object",
|
|
||||||
nullable: true,
|
|
||||||
optional: true,
|
|
||||||
ref: "User",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import define from "@/server/api/define.js";
|
import define from "@/server/api/define.js";
|
||||||
import { Emojis } from "@/models/index.js";
|
import { Emojis } from "@/models/index.js";
|
||||||
import { toPuny } from "backend-rs";
|
import { sqlLikeEscape, toPuny } from "backend-rs";
|
||||||
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
||||||
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
|
|
||||||
import { ApiError } from "@/server/api/error.js";
|
import { ApiError } from "@/server/api/error.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import define from "@/server/api/define.js";
|
import define from "@/server/api/define.js";
|
||||||
import { Emojis } from "@/models/index.js";
|
import { Emojis } from "@/models/index.js";
|
||||||
import { makePaginationQuery } from "../../../common/make-pagination-query.js";
|
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
||||||
import type { Emoji } from "@/models/entities/emoji.js";
|
import type { Emoji } from "@/models/entities/emoji.js";
|
||||||
//import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
|
//import { sqlLikeEscape } from "backend-rs";
|
||||||
import { ApiError } from "@/server/api/error.js";
|
import { ApiError } from "@/server/api/error.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "backend-rs";
|
||||||
import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "@/const.js";
|
import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "@/const.js";
|
||||||
import define from "@/server/api/define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
|
||||||
|
@ -24,6 +24,11 @@ export const meta = {
|
||||||
optional: false,
|
optional: false,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
|
antennaLimit: {
|
||||||
|
type: "number",
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
cacheRemoteFiles: {
|
cacheRemoteFiles: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
optional: false,
|
optional: false,
|
||||||
|
@ -466,7 +471,7 @@ export const paramDef = {
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export default define(meta, paramDef, async () => {
|
export default define(meta, paramDef, async () => {
|
||||||
const instance = await fetchMeta(true);
|
const instance = await fetchMeta(false);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
maintainerName: instance.maintainerName,
|
maintainerName: instance.maintainerName,
|
||||||
|
@ -487,6 +492,7 @@ export default define(meta, paramDef, async () => {
|
||||||
enableGuestTimeline: instance.enableGuestTimeline,
|
enableGuestTimeline: instance.enableGuestTimeline,
|
||||||
driveCapacityPerLocalUserMb: instance.localDriveCapacityMb,
|
driveCapacityPerLocalUserMb: instance.localDriveCapacityMb,
|
||||||
driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb,
|
driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb,
|
||||||
|
antennaLimit: instance.antennaLimit,
|
||||||
emailRequiredForSignup: instance.emailRequiredForSignup,
|
emailRequiredForSignup: instance.emailRequiredForSignup,
|
||||||
enableHcaptcha: instance.enableHcaptcha,
|
enableHcaptcha: instance.enableHcaptcha,
|
||||||
hcaptchaSiteKey: instance.hcaptchaSiteKey,
|
hcaptchaSiteKey: instance.hcaptchaSiteKey,
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue