Merge branch 'develop' of https://firefish.dev/firefish/firefish into refactor/types
This commit is contained in:
commit
f95e2d1c51
188 changed files with 3095 additions and 1557 deletions
|
@ -10,7 +10,7 @@ node_modules
|
|||
report.*.json
|
||||
|
||||
# Rust
|
||||
packages/backend-rs/target
|
||||
/target
|
||||
|
||||
# Coverage
|
||||
coverage
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -14,6 +14,9 @@ packages/backend/.idea/vcs.xml
|
|||
node_modules
|
||||
report.*.json
|
||||
|
||||
# Cargo
|
||||
/target
|
||||
|
||||
# Cypress
|
||||
cypress/screenshots
|
||||
cypress/videos
|
||||
|
|
|
@ -28,5 +28,5 @@ By submitting this issue, you agree to follow our [Contribution Guidelines](http
|
|||
- [ ] I agree to follow this project's Contribution Guidelines
|
||||
- [ ] I have searched the issue tracker for similar issues, and this is not a duplicate.
|
||||
|
||||
**Can you 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.
|
||||
|
|
|
@ -16,5 +16,5 @@ By submitting this issue, you agree to follow our [Contribution Guidelines](http
|
|||
- [ ] I agree to follow this project's Contribution Guidelines
|
||||
- [ ] I have searched the issue tracker for similar requests, and this is not a duplicate.
|
||||
|
||||
**Can you 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.
|
||||
|
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"rust-analyzer.linkedProjects": [
|
||||
"packages/backend-rs/Cargo.toml"
|
||||
]
|
||||
}
|
124
packages/backend-rs/Cargo.lock → Cargo.lock
generated
124
packages/backend-rs/Cargo.lock → Cargo.lock
generated
|
@ -128,9 +128,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.81"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
||||
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
|
@ -162,9 +162,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.79"
|
||||
version = "0.1.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681"
|
||||
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -195,7 +195,9 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"chrono",
|
||||
"cuid2",
|
||||
"idna",
|
||||
"jsonschema",
|
||||
"macro_rs",
|
||||
"napi",
|
||||
"napi-build",
|
||||
"napi-derive",
|
||||
|
@ -203,12 +205,16 @@ dependencies = [
|
|||
"parse-display",
|
||||
"pretty_assertions",
|
||||
"rand",
|
||||
"regex",
|
||||
"schemars",
|
||||
"sea-orm",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"url",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -332,9 +338,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.15.4"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck"
|
||||
|
@ -378,9 +384,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.90"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
|
||||
checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -497,9 +503,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.0.1"
|
||||
version = "3.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
|
||||
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
|
||||
dependencies = [
|
||||
"crc-catalog",
|
||||
]
|
||||
|
@ -636,9 +642,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.33"
|
||||
version = "0.8.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
|
||||
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
@ -839,9 +845,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.13"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06fddc2749e0528d2813f95e050e87e52c8cbbae56223b9babf73b3e53b0cc6"
|
||||
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
|
@ -1211,6 +1217,18 @@ version = "0.4.21"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
|
||||
[[package]]
|
||||
name = "macro_rs"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"napi",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.6"
|
||||
|
@ -1261,9 +1279,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "napi"
|
||||
version = "2.16.1"
|
||||
version = "2.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4ca998356d8ff9fba7a070dae4508a2298439c98c9f3bc9c07669538b999e8f"
|
||||
checksum = "70d04890ef4ec001fad791be785b8b920e2a3f5a6b1188e7a81dfa6197c0dee4"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"chrono",
|
||||
|
@ -1284,9 +1302,9 @@ checksum = "2f9130fccc5f763cf2069b34a089a18f0d0883c66aceb81f2fad541a3d823c43"
|
|||
|
||||
[[package]]
|
||||
name = "napi-derive"
|
||||
version = "2.16.1"
|
||||
version = "2.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b138cecf1141ae0ff5d62f4aa0e2f269aec339f66070f346ba6fb4279f1fc178"
|
||||
checksum = "aff4a63f26a7aa9fa25df6ea36675890115972b207ae516e86bd0c47e31eba39"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"convert_case",
|
||||
|
@ -1298,9 +1316,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "napi-derive-backend"
|
||||
version = "1.0.63"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce5126b64f6ad9e28e30e6d15213dd378626b38f556454afebc42f7f02a90902"
|
||||
checksum = "e5e76afe642e2424ebe465406e328e4316d82af1f79256231d4b0f184c67550a"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"once_cell",
|
||||
|
@ -1313,9 +1331,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "napi-sys"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2503fa6af34dc83fb74888df8b22afe933b58d37daf7d80424b1c60c68196b8b"
|
||||
checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3"
|
||||
dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
@ -1518,26 +1536,25 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "parse-display"
|
||||
version = "0.8.2"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6509d08722b53e8dafe97f2027b22ccbe3a5db83cb352931e9716b0aa44bc5c"
|
||||
checksum = "06af5f9333eb47bd9ba8462d612e37a8328a5cb80b13f0af4de4c3b89f52dee5"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"parse-display-derive",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse-display-derive"
|
||||
version = "0.8.2"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68517892c8daf78da08c0db777fcc17e07f2f63ef70041718f8a7630ad84f341"
|
||||
checksum = "dc9252f259500ee570c75adcc4e317fa6f57a1e47747d622e0bf838002a7b790"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"regex-syntax 0.7.5",
|
||||
"regex-syntax",
|
||||
"structmeta",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
@ -1688,9 +1705,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -1749,7 +1766,7 @@ dependencies = [
|
|||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax 0.8.3",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1760,15 +1777,9 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
|
|||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.3",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.3"
|
||||
|
@ -2150,6 +2161,19 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.34+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
|
@ -2509,9 +2533,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||
|
||||
[[package]]
|
||||
name = "structmeta"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ad9e09554f0456d67a69c1584c9798ba733a5b50349a6c0d0948710523922d"
|
||||
checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2521,9 +2545,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "structmeta-derive"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
|
||||
checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2643,9 +2667,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.34"
|
||||
version = "0.3.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
|
||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
|
@ -2664,9 +2688,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
|||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.17"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
|
@ -2842,6 +2866,12 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
39
Cargo.toml
Normal file
39
Cargo.toml
Normal file
|
@ -0,0 +1,39 @@
|
|||
[workspace]
|
||||
members = ["packages/backend-rs", "packages/macro-rs"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.dependencies]
|
||||
macro_rs = { path = "packages/macro-rs" }
|
||||
|
||||
napi = { version = "2.16.2", default-features = false }
|
||||
napi-derive = "2.16.2"
|
||||
napi-build = "2.1.2"
|
||||
|
||||
async-trait = "0.1.80"
|
||||
basen = "0.1.0"
|
||||
cfg-if = "1.0.0"
|
||||
chrono = "0.4.37"
|
||||
convert_case = "0.6.0"
|
||||
cuid2 = "0.1.2"
|
||||
idna = "0.5.0"
|
||||
jsonschema = "0.17.1"
|
||||
once_cell = "1.19.0"
|
||||
parse-display = "0.9.0"
|
||||
pretty_assertions = "1.4.0"
|
||||
proc-macro2 = "1.0.79"
|
||||
quote = "1.0.36"
|
||||
rand = "0.8.5"
|
||||
regex = "1.10.4"
|
||||
schemars = "0.8.16"
|
||||
sea-orm = "0.12.15"
|
||||
serde = "1.0.197"
|
||||
serde_json = "1.0.115"
|
||||
serde_yaml = "0.9.34"
|
||||
syn = "2.0.58"
|
||||
thiserror = "1.0.58"
|
||||
tokio = "1.37.0"
|
||||
url = "2.5.0"
|
||||
urlencoding = "2.1.3"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
|
@ -8,9 +8,12 @@ RUN curl --proto '=https' --tlsv1.2 --silent --show-error --fail https://sh.rust
|
|||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
|
||||
# Copy only the cargo dependency-related files first, to cache efficiently
|
||||
COPY Cargo.toml Cargo.toml
|
||||
COPY Cargo.lock Cargo.lock
|
||||
COPY packages/backend-rs/Cargo.toml packages/backend-rs/Cargo.toml
|
||||
COPY packages/backend-rs/Cargo.lock packages/backend-rs/Cargo.lock
|
||||
COPY packages/backend-rs/src/lib.rs packages/backend-rs/src/
|
||||
COPY packages/macro-rs/Cargo.toml packages/macro-rs/Cargo.toml
|
||||
COPY packages/macro-rs/src/lib.rs packages/macro-rs/src/
|
||||
|
||||
# Install cargo dependencies
|
||||
RUN cargo fetch --locked --manifest-path /firefish/packages/backend-rs/Cargo.toml
|
||||
|
|
8
Makefile
8
Makefile
|
@ -3,7 +3,7 @@ export
|
|||
|
||||
|
||||
.PHONY: pre-commit
|
||||
pre-commit: format entities index-js
|
||||
pre-commit: format entities napi-index
|
||||
|
||||
.PHONY: format
|
||||
format:
|
||||
|
@ -14,9 +14,9 @@ entities:
|
|||
pnpm run migrate
|
||||
$(MAKE) -C ./packages/backend-rs regenerate-entities
|
||||
|
||||
.PHONY: index-js
|
||||
index-js:
|
||||
$(MAKE) -C ./packages/backend-rs index.js
|
||||
.PHONY: napi-index
|
||||
napi-index:
|
||||
$(MAKE) -C ./packages/backend-rs update-index
|
||||
|
||||
|
||||
.PHONY: build
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
BEGIN;
|
||||
|
||||
DELETE FROM "migrations" WHERE name IN (
|
||||
'FixChatFileConstraint1712855579316',
|
||||
'DropTimeZone1712425488543',
|
||||
'ExpandNoteEdit1711936358554',
|
||||
'markLocalFilesNsfwByDefault1709305200000',
|
||||
|
@ -21,6 +22,10 @@ DELETE FROM "migrations" WHERE name IN (
|
|||
'RemoveNativeUtilsMigration1705877093218'
|
||||
);
|
||||
|
||||
-- fix-chat-file-constraint
|
||||
ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_535def119223ac05ad3fa9ef64b";
|
||||
ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_535def119223ac05ad3fa9ef64b" FOREIGN KEY ("fileId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION;
|
||||
|
||||
-- drop-time-zone
|
||||
ALTER TABLE "abuse_user_report" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "access_token" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
|
|
|
@ -19,7 +19,11 @@ The number of posts stored on your database can be found at `https://yourserver.
|
|||
|
||||
### For systemd/pm2 users
|
||||
|
||||
Please do not terminate `pnpm run migrate` even if it appears to be frozen.
|
||||
- Please remove `packages/backend-rs/target` before building Firefish.
|
||||
```sh
|
||||
rm --recursive --force packages/backend-rs/target
|
||||
```
|
||||
- Please do not terminate `pnpm run migrate` even if it appears to be frozen.
|
||||
|
||||
### For Docker/Podman users
|
||||
|
||||
|
|
|
@ -2084,9 +2084,9 @@ _experiments:
|
|||
release: Publicà
|
||||
title: Experiments
|
||||
enablePostImports: Activar l'importació de publicacions
|
||||
postImportsCaption: Permet els usuaris importar publicacions desde comptes a Firefish,
|
||||
postImportsCaption: Permet als usuaris importar publicacions des de comptes de Firefish,
|
||||
Misskey, Mastodon, Akkoma i Pleroma. Pot fer que el servidor vagi més lent durant
|
||||
la càrrega si tens un coll d'ampolla a la cua.
|
||||
la importació si la teva cua de feina és saturada.
|
||||
noGraze: Si us plau, desactiva l'extensió del navegador "Graze for Mastodon", ja que
|
||||
interfereix amb Firefish.
|
||||
accessibility: Accessibilitat
|
||||
|
|
14
package.json
14
package.json
|
@ -20,15 +20,19 @@
|
|||
"watch": "pnpm run dev",
|
||||
"dev": "pnpm node ./scripts/dev.mjs",
|
||||
"dev:staging": "NODE_OPTIONS=--max_old_space_size=3072 NODE_ENV=development pnpm run build && pnpm run start",
|
||||
"lint": "pnpm --filter !megalodon --filter !firefish-js -r --parallel run lint",
|
||||
"lint": "pnpm run lint:ts; pnpm run lint:rs",
|
||||
"lint:ts": "pnpm --filter !firefish-js -r --parallel run lint",
|
||||
"lint:rs": "cargo clippy --fix --allow-dirty --allow-staged && cargo fmt --all --",
|
||||
"debug": "pnpm run build:debug && pnpm run start",
|
||||
"build:debug": "pnpm run clean && pnpm node ./scripts/dev-build.mjs && pnpm run gulp",
|
||||
"mocha": "pnpm --filter backend run mocha",
|
||||
"test": "pnpm run mocha",
|
||||
"format": "pnpm -r --parallel run format",
|
||||
"format": "pnpm run format:ts; pnpm run format:rs",
|
||||
"format:ts": "pnpm -r --parallel run format",
|
||||
"format:rs": "cargo fmt --all --",
|
||||
"clean": "pnpm node ./scripts/clean-built.mjs",
|
||||
"clean-npm": "pnpm node ./scripts/clean-npm.mjs",
|
||||
"clean-cargo": "pnpm node ./scripts/clean-cargo.mjs",
|
||||
"clean-cargo": "cargo clean",
|
||||
"clean-all": "pnpm run clean && pnpm run clean-cargo && pnpm run clean-npm"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -44,9 +48,9 @@
|
|||
"@biomejs/cli-darwin-x64": "^1.6.4",
|
||||
"@biomejs/cli-linux-arm64": "^1.6.4",
|
||||
"@biomejs/cli-linux-x64": "^1.6.4",
|
||||
"@types/node": "20.12.5",
|
||||
"@types/node": "20.12.7",
|
||||
"execa": "8.0.1",
|
||||
"pnpm": "8.15.6",
|
||||
"typescript": "5.4.4"
|
||||
"typescript": "5.4.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,31 +12,34 @@ napi = ["dep:napi", "dep:napi-derive"]
|
|||
crate-type = ["cdylib", "lib"]
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.79"
|
||||
cfg-if = "1.0.0"
|
||||
chrono = "0.4.37"
|
||||
cuid2 = "0.1.2"
|
||||
jsonschema = "0.17.1"
|
||||
once_cell = "1.19.0"
|
||||
parse-display = "0.8.2"
|
||||
rand = "0.8.5"
|
||||
schemars = { version = "0.8.16", features = ["chrono"] }
|
||||
sea-orm = { version = "0.12.15", features = ["sqlx-postgres", "runtime-tokio-rustls"] }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
serde_json = "1.0.115"
|
||||
thiserror = "1.0.58"
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
macro_rs = { workspace = true }
|
||||
|
||||
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
|
||||
napi = { version = "2.16.1", default-features = false, features = ["napi9", "tokio_rt", "chrono_date", "serde-json"], optional = true }
|
||||
napi-derive = { version = "2.16.1", optional = true }
|
||||
basen = "0.1.0"
|
||||
napi = { workspace = true, optional = true, default-features = false, features = ["napi9", "tokio_rt", "chrono_date", "serde-json"] }
|
||||
napi-derive = { workspace = true, optional = true }
|
||||
|
||||
async-trait = { workspace = true }
|
||||
basen = { workspace = true }
|
||||
cfg-if = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
cuid2 = { workspace = true }
|
||||
idna = { workspace = true }
|
||||
jsonschema = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
parse-display = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
schemars = { workspace = true, features = ["chrono"] }
|
||||
sea-orm = { workspace = true, features = ["sqlx-postgres", "runtime-tokio-rustls"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
serde_yaml = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
url = { workspace = true }
|
||||
urlencoding = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.0"
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = "2.1.2"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
napi-build = { workspace = true }
|
||||
|
|
|
@ -19,10 +19,15 @@ regenerate-entities:
|
|||
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)]/' \
|
||||
src/model/entity/sea_orm_active_enums.rs
|
||||
pnpm run format
|
||||
cargo fmt --all --
|
||||
|
||||
index.js: $(SRC)
|
||||
.PHONY: update-index
|
||||
update-index: index.js index.d.ts
|
||||
|
||||
index.js index.d.ts: $(SRC)
|
||||
NODE_OPTIONS='--max_old_space_size=3072' pnpm run build:debug
|
||||
[ -f built/index.js ]
|
||||
rm --force index.js
|
||||
[ -f built/index.js ] && [ -f built/index.d.ts ]
|
||||
rm --force index.js index.d.ts
|
||||
cp built/index.js index.js
|
||||
cp built/index.d.ts index.d.ts
|
||||
sed -i 's/^ \*r"/ */g' index.d.ts
|
||||
|
|
1000
packages/backend-rs/index.d.ts
vendored
Normal file
1000
packages/backend-rs/index.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -224,17 +224,32 @@ switch (platform) {
|
|||
}
|
||||
break
|
||||
case 'arm':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'backend-rs.linux-arm-gnueabihf.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./backend-rs.linux-arm-gnueabihf.node')
|
||||
} else {
|
||||
nativeBinding = require('backend-rs-linux-arm-gnueabihf')
|
||||
if (isMusl()) {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'backend-rs.linux-arm-musleabihf.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./backend-rs.linux-arm-musleabihf.node')
|
||||
} else {
|
||||
nativeBinding = require('backend-rs-linux-arm-musleabihf')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
} else {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'backend-rs.linux-arm-gnueabihf.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./backend-rs.linux-arm-gnueabihf.node')
|
||||
} else {
|
||||
nativeBinding = require('backend-rs-linux-arm-gnueabihf')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'riscv64':
|
||||
|
@ -295,8 +310,20 @@ if (!nativeBinding) {
|
|||
throw new Error(`Failed to load native binding`)
|
||||
}
|
||||
|
||||
const { AntennaSrcEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, nativeRandomStr, IdConvertType, convertId, nativeGetTimestamp, nativeCreateId, nativeInitIdGenerator } = nativeBinding
|
||||
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
|
||||
|
||||
module.exports.readServerConfig = readServerConfig
|
||||
module.exports.stringToAcct = stringToAcct
|
||||
module.exports.acctToString = acctToString
|
||||
module.exports.checkWordMute = checkWordMute
|
||||
module.exports.getFullApAccount = getFullApAccount
|
||||
module.exports.isSelfHost = isSelfHost
|
||||
module.exports.isSameOrigin = isSameOrigin
|
||||
module.exports.extractHost = extractHost
|
||||
module.exports.toPuny = toPuny
|
||||
module.exports.toMastodonId = toMastodonId
|
||||
module.exports.fromMastodonId = fromMastodonId
|
||||
module.exports.nyaify = nyaify
|
||||
module.exports.AntennaSrcEnum = AntennaSrcEnum
|
||||
module.exports.MutedNoteReasonEnum = MutedNoteReasonEnum
|
||||
module.exports.NoteVisibilityEnum = NoteVisibilityEnum
|
||||
|
@ -307,9 +334,7 @@ module.exports.RelayStatusEnum = RelayStatusEnum
|
|||
module.exports.UserEmojimodpermEnum = UserEmojimodpermEnum
|
||||
module.exports.UserProfileFfvisibilityEnum = UserProfileFfvisibilityEnum
|
||||
module.exports.UserProfileMutingnotificationtypesEnum = UserProfileMutingnotificationtypesEnum
|
||||
module.exports.nativeRandomStr = nativeRandomStr
|
||||
module.exports.IdConvertType = IdConvertType
|
||||
module.exports.convertId = convertId
|
||||
module.exports.nativeGetTimestamp = nativeGetTimestamp
|
||||
module.exports.nativeCreateId = nativeCreateId
|
||||
module.exports.nativeInitIdGenerator = nativeInitIdGenerator
|
||||
module.exports.initIdGenerator = initIdGenerator
|
||||
module.exports.getTimestamp = getTimestamp
|
||||
module.exports.genId = genId
|
||||
module.exports.secureRndstr = secureRndstr
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@napi-rs/cli": "2.18.0",
|
||||
"@napi-rs/cli": "2.18.1",
|
||||
"ava": "6.1.2"
|
||||
},
|
||||
"ava": {
|
||||
|
@ -39,8 +39,6 @@
|
|||
"test": "pnpm run cargo:test && pnpm run build:debug && ava",
|
||||
"universal": "napi universal",
|
||||
"version": "napi version",
|
||||
"format": "cargo fmt --all --",
|
||||
"lint": "cargo clippy --fix --allow-dirty --allow-staged && cargo fmt --all --",
|
||||
"cargo:test": "pnpm run cargo:unit && pnpm run cargo:integration",
|
||||
"cargo:unit": "cargo test unit_test && cargo test -F napi unit_test",
|
||||
"cargo:integration": "cargo test int_test"
|
||||
|
|
1
packages/backend-rs/src/config/mod.rs
Normal file
1
packages/backend-rs/src/config/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod server;
|
183
packages/backend-rs/src/config/server.rs
Normal file
183
packages/backend-rs/src/config/server.rs
Normal file
|
@ -0,0 +1,183 @@
|
|||
use once_cell::sync::Lazy;
|
||||
use serde::Deserialize;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
pub struct ServerConfig {
|
||||
pub url: String,
|
||||
pub port: u16,
|
||||
/// host to listen on
|
||||
pub bind: Option<String>,
|
||||
pub disable_hsts: Option<bool>,
|
||||
|
||||
pub db: DbConfig,
|
||||
pub redis: RedisConfig,
|
||||
pub cache_server: Option<RedisConfig>,
|
||||
|
||||
pub proxy: Option<String>,
|
||||
pub proxy_smtp: Option<String>,
|
||||
pub proxy_bypass_hosts: Option<Vec<String>>,
|
||||
|
||||
pub allowed_private_networks: Option<Vec<String>>,
|
||||
/// `NapiValue` is not implemented for `u64`
|
||||
pub max_file_size: Option<i64>,
|
||||
pub access_log: Option<String>,
|
||||
pub cluster_limits: Option<WorkerConfig>,
|
||||
pub cuid: Option<IdConfig>,
|
||||
pub outgoing_address: Option<String>,
|
||||
|
||||
pub deliver_job_concurrency: Option<u32>,
|
||||
pub inbox_job_concurrency: Option<u32>,
|
||||
pub deliver_job_per_sec: Option<u32>,
|
||||
pub inbox_job_per_sec: Option<u32>,
|
||||
pub deliver_job_max_attempts: Option<u32>,
|
||||
pub inbox_job_max_attempts: Option<u32>,
|
||||
|
||||
pub log_level: Option<Vec<String>>,
|
||||
|
||||
pub syslog: Option<SysLogConfig>,
|
||||
|
||||
pub proxy_remote_files: Option<bool>,
|
||||
pub media_proxy: Option<String>,
|
||||
pub summaly_proxy_url: Option<String>,
|
||||
|
||||
pub reserved_usernames: Option<Vec<String>>,
|
||||
|
||||
pub max_user_signups: Option<u32>,
|
||||
pub is_managed_hosting: Option<bool>,
|
||||
pub max_note_length: Option<u32>,
|
||||
pub max_caption_length: Option<u32>,
|
||||
|
||||
pub deepl: Option<DeepLConfig>,
|
||||
pub libre_translate: Option<LibreTranslateConfig>,
|
||||
pub email: Option<EmailConfig>,
|
||||
pub object_storage: Option<ObjectStorageConfig>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
pub struct DbConfig {
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub db: String,
|
||||
pub user: String,
|
||||
pub pass: String,
|
||||
pub disable_cache: Option<bool>,
|
||||
pub extra: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
pub struct RedisConfig {
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub family: Option<u8>,
|
||||
pub user: Option<String>,
|
||||
pub pass: Option<String>,
|
||||
pub tls: Option<TlsConfig>,
|
||||
#[serde(default)]
|
||||
pub db: u32,
|
||||
#[serde(default)]
|
||||
pub prefix: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
pub struct TlsConfig {
|
||||
pub host: String,
|
||||
pub reject_unauthorized: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
pub struct WorkerConfig {
|
||||
pub web: Option<u32>,
|
||||
pub queue: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
pub struct IdConfig {
|
||||
pub length: Option<u8>,
|
||||
pub fingerprint: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
pub struct SysLogConfig {
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
pub struct DeepLConfig {
|
||||
pub managed: Option<bool>,
|
||||
pub auth_key: Option<String>,
|
||||
pub is_pro: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
pub struct LibreTranslateConfig {
|
||||
pub managed: Option<bool>,
|
||||
pub api_url: Option<String>,
|
||||
pub api_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
pub struct EmailConfig {
|
||||
pub managed: Option<bool>,
|
||||
pub address: Option<String>,
|
||||
pub host: Option<String>,
|
||||
pub port: Option<u16>,
|
||||
pub user: Option<String>,
|
||||
pub pass: Option<String>,
|
||||
pub use_implicit_ssl_tls: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
pub struct ObjectStorageConfig {
|
||||
pub managed: Option<bool>,
|
||||
pub base_url: Option<String>,
|
||||
pub bucket: Option<String>,
|
||||
pub prefix: Option<String>,
|
||||
pub endpoint: Option<String>,
|
||||
pub region: Option<String>,
|
||||
pub access_key: Option<String>,
|
||||
pub secret_key: Option<String>,
|
||||
pub use_ssl: Option<bool>,
|
||||
pub connnect_over_proxy: Option<bool>,
|
||||
pub set_public_read_on_upload: Option<bool>,
|
||||
pub s3_force_path_style: Option<bool>,
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn read_server_config() -> ServerConfig {
|
||||
let cwd = env::current_dir().unwrap();
|
||||
let yml = fs::File::open(cwd.join("../../.config/default.yml"))
|
||||
.expect("Failed to open '.config/default.yml'");
|
||||
let mut data: ServerConfig = serde_yaml::from_reader(yml).expect("Failed to parse yaml");
|
||||
data.url = url::Url::parse(&data.url)
|
||||
.expect("Config url is invalid")
|
||||
.origin()
|
||||
.ascii_serialization();
|
||||
data
|
||||
}
|
||||
|
||||
pub static SERVER_CONFIG: Lazy<ServerConfig> = Lazy::new(read_server_config);
|
|
@ -1,13 +0,0 @@
|
|||
use sea_orm::error::DbErr;
|
||||
|
||||
use crate::impl_into_napi_error;
|
||||
|
||||
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
#[error("The database connections have not been initialized yet")]
|
||||
Uninitialized,
|
||||
#[error("ORM error: {0}")]
|
||||
OrmError(#[from] DbErr),
|
||||
}
|
||||
|
||||
impl_into_napi_error!(Error);
|
|
@ -1,26 +1,34 @@
|
|||
pub mod error;
|
||||
|
||||
use error::Error;
|
||||
use sea_orm::{Database, DbConn};
|
||||
use crate::config::server::SERVER_CONFIG;
|
||||
use sea_orm::{Database, DbConn, DbErr};
|
||||
|
||||
static DB_CONN: once_cell::sync::OnceCell<DbConn> = once_cell::sync::OnceCell::new();
|
||||
|
||||
pub async fn init_database(conn_uri: impl Into<String>) -> Result<(), Error> {
|
||||
let conn = Database::connect(conn_uri.into()).await?;
|
||||
DB_CONN.get_or_init(move || conn);
|
||||
Ok(())
|
||||
async fn init_database() -> Result<&'static DbConn, DbErr> {
|
||||
let database_uri = format!(
|
||||
"postgres://{}:{}@{}:{}/{}",
|
||||
SERVER_CONFIG.db.user,
|
||||
urlencoding::encode(&SERVER_CONFIG.db.pass),
|
||||
SERVER_CONFIG.db.host,
|
||||
SERVER_CONFIG.db.port,
|
||||
SERVER_CONFIG.db.db,
|
||||
);
|
||||
let conn = Database::connect(database_uri).await?;
|
||||
Ok(DB_CONN.get_or_init(move || conn))
|
||||
}
|
||||
|
||||
pub fn get_database() -> Result<&'static DbConn, Error> {
|
||||
DB_CONN.get().ok_or(Error::Uninitialized)
|
||||
pub async fn db_conn() -> Result<&'static DbConn, DbErr> {
|
||||
match DB_CONN.get() {
|
||||
Some(conn) => Ok(conn),
|
||||
None => init_database().await,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::{error::Error, get_database};
|
||||
use super::db_conn;
|
||||
|
||||
#[test]
|
||||
fn error_uninitialized() {
|
||||
assert_eq!(get_database().unwrap_err(), Error::Uninitialized);
|
||||
#[tokio::test]
|
||||
async fn connect_test() {
|
||||
assert!(db_conn().await.is_ok());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
pub use macro_rs::export;
|
||||
|
||||
pub mod config;
|
||||
pub mod database;
|
||||
pub mod macros;
|
||||
pub mod misc;
|
||||
pub mod model;
|
||||
pub mod util;
|
||||
|
||||
#[cfg(feature = "napi")]
|
||||
pub mod mastodon_api;
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
#[macro_export]
|
||||
macro_rules! impl_into_napi_error {
|
||||
($a:ty) => {
|
||||
#[cfg(feature = "napi")]
|
||||
impl Into<napi::Error> for $a {
|
||||
fn into(self) -> napi::Error {
|
||||
napi::Error::from_reason(self.to_string())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
use napi::{Error, Status};
|
||||
use napi_derive::napi;
|
||||
|
||||
static CHAR_COLLECTION: &str = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
// -- NAPI exports --
|
||||
|
||||
#[napi]
|
||||
pub enum IdConvertType {
|
||||
MastodonId,
|
||||
FirefishId,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn convert_id(in_id: String, id_convert_type: IdConvertType) -> napi::Result<String> {
|
||||
use IdConvertType::*;
|
||||
match id_convert_type {
|
||||
MastodonId => {
|
||||
let mut out: i128 = 0;
|
||||
for (i, c) in in_id.to_lowercase().chars().rev().enumerate() {
|
||||
out += num_from_char(c)? as i128 * 36_i128.pow(i as u32);
|
||||
}
|
||||
|
||||
Ok(out.to_string())
|
||||
}
|
||||
FirefishId => {
|
||||
let mut input: i128 = match in_id.parse() {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
return Err(Error::new(
|
||||
Status::InvalidArg,
|
||||
"Unable to parse ID as MastodonId",
|
||||
))
|
||||
}
|
||||
};
|
||||
let mut out = String::new();
|
||||
|
||||
while input != 0 {
|
||||
out.insert(0, char_from_num((input % 36) as u8)?);
|
||||
input /= 36;
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- end --
|
||||
|
||||
#[inline(always)]
|
||||
fn num_from_char(character: char) -> napi::Result<u8> {
|
||||
for (i, c) in CHAR_COLLECTION.chars().enumerate() {
|
||||
if c == character {
|
||||
return Ok(i as u8);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::new(
|
||||
Status::InvalidArg,
|
||||
"Invalid character in parsed base36 id",
|
||||
))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn char_from_num(number: u8) -> napi::Result<char> {
|
||||
CHAR_COLLECTION
|
||||
.chars()
|
||||
.nth(number as usize)
|
||||
.ok_or(Error::from_status(Status::Unknown))
|
||||
}
|
74
packages/backend-rs/src/misc/acct.rs
Normal file
74
packages/backend-rs/src/misc/acct.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
#[derive(Debug, PartialEq)]
|
||||
#[crate::export(object)]
|
||||
pub struct Acct {
|
||||
pub username: String,
|
||||
pub host: Option<String>,
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn string_to_acct(acct: &str) -> Acct {
|
||||
let split: Vec<&str> = if let Some(stripped) = acct.strip_prefix('@') {
|
||||
stripped
|
||||
} else {
|
||||
acct
|
||||
}
|
||||
.split('@')
|
||||
.collect();
|
||||
|
||||
Acct {
|
||||
username: split[0].to_string(),
|
||||
host: if split.len() == 1 {
|
||||
None
|
||||
} else {
|
||||
Some(split[1].to_string())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn acct_to_string(acct: &Acct) -> String {
|
||||
match &acct.host {
|
||||
Some(host) => format!("{}@{}", acct.username, host),
|
||||
None => acct.username.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::{acct_to_string, string_to_acct, Acct};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_acct_to_string() {
|
||||
let remote_acct = Acct {
|
||||
username: "firefish".to_string(),
|
||||
host: Some("example.com".to_string()),
|
||||
};
|
||||
let local_acct = Acct {
|
||||
username: "MisakaMikoto".to_string(),
|
||||
host: None,
|
||||
};
|
||||
|
||||
assert_eq!(acct_to_string(&remote_acct), "firefish@example.com");
|
||||
assert_ne!(acct_to_string(&remote_acct), "mastodon@example.com");
|
||||
assert_eq!(acct_to_string(&local_acct), "MisakaMikoto");
|
||||
assert_ne!(acct_to_string(&local_acct), "ShiraiKuroko");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_to_acct() {
|
||||
let remote_acct = Acct {
|
||||
username: "firefish".to_string(),
|
||||
host: Some("example.com".to_string()),
|
||||
};
|
||||
let local_acct = Acct {
|
||||
username: "MisakaMikoto".to_string(),
|
||||
host: None,
|
||||
};
|
||||
|
||||
assert_eq!(string_to_acct("@firefish@example.com"), remote_acct);
|
||||
assert_eq!(string_to_acct("firefish@example.com"), remote_acct);
|
||||
assert_eq!(string_to_acct("@MisakaMikoto"), local_acct);
|
||||
assert_eq!(string_to_acct("MisakaMikoto"), local_acct);
|
||||
}
|
||||
}
|
117
packages/backend-rs/src/misc/check_word_mute.rs
Normal file
117
packages/backend-rs/src/misc/check_word_mute.rs
Normal file
|
@ -0,0 +1,117 @@
|
|||
use crate::database::db_conn;
|
||||
use crate::model::entity::{drive_file, note};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use sea_orm::{prelude::*, QuerySelect};
|
||||
|
||||
#[crate::export(object)]
|
||||
pub struct NoteLike {
|
||||
pub file_ids: Vec<String>,
|
||||
pub user_id: Option<String>,
|
||||
pub text: Option<String>,
|
||||
pub cw: Option<String>,
|
||||
pub renote_id: Option<String>,
|
||||
pub reply_id: Option<String>,
|
||||
}
|
||||
|
||||
async fn all_texts(note: NoteLike) -> Result<Vec<String>, DbErr> {
|
||||
let db = db_conn().await?;
|
||||
|
||||
let mut texts: Vec<String> = vec![];
|
||||
|
||||
if let Some(text) = note.text {
|
||||
texts.push(text);
|
||||
}
|
||||
if let Some(cw) = note.cw {
|
||||
texts.push(cw);
|
||||
}
|
||||
|
||||
texts.extend(
|
||||
drive_file::Entity::find()
|
||||
.select_only()
|
||||
.column(drive_file::Column::Comment)
|
||||
.filter(drive_file::Column::Id.is_in(note.file_ids))
|
||||
.into_tuple::<Option<String>>()
|
||||
.all(db)
|
||||
.await?
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
);
|
||||
|
||||
if let Some(renote_id) = note.renote_id {
|
||||
if let Some((text, cw)) = note::Entity::find_by_id(renote_id)
|
||||
.select_only()
|
||||
.columns([note::Column::Text, note::Column::Cw])
|
||||
.into_tuple::<(Option<String>, Option<String>)>()
|
||||
.one(db)
|
||||
.await?
|
||||
{
|
||||
if let Some(t) = text {
|
||||
texts.push(t);
|
||||
}
|
||||
if let Some(c) = cw {
|
||||
texts.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(reply_id) = note.reply_id {
|
||||
if let Some((text, cw)) = note::Entity::find_by_id(reply_id)
|
||||
.select_only()
|
||||
.columns([note::Column::Text, note::Column::Cw])
|
||||
.into_tuple::<(Option<String>, Option<String>)>()
|
||||
.one(db)
|
||||
.await?
|
||||
{
|
||||
if let Some(t) = text {
|
||||
texts.push(t);
|
||||
}
|
||||
if let Some(c) = cw {
|
||||
texts.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(texts)
|
||||
}
|
||||
|
||||
fn convert_regex(js_regex: &str) -> String {
|
||||
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^/(.+)/(.*)$").unwrap());
|
||||
RE.replace(js_regex, "(?$2)$1").to_string()
|
||||
}
|
||||
|
||||
fn check_word_mute_impl(
|
||||
texts: &[String],
|
||||
muted_word_lists: &[Vec<String>],
|
||||
muted_patterns: &[String],
|
||||
) -> bool {
|
||||
muted_word_lists.iter().any(|muted_word_list| {
|
||||
texts.iter().any(|text| {
|
||||
let text_lower = text.to_lowercase();
|
||||
muted_word_list
|
||||
.iter()
|
||||
.all(|muted_word| text_lower.contains(&muted_word.to_lowercase()))
|
||||
})
|
||||
}) || muted_patterns.iter().any(|muted_pattern| {
|
||||
Regex::new(convert_regex(muted_pattern).as_str())
|
||||
.map(|re| texts.iter().any(|text| re.is_match(text)))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub async fn check_word_mute(
|
||||
note: NoteLike,
|
||||
muted_word_lists: Vec<Vec<String>>,
|
||||
muted_patterns: Vec<String>,
|
||||
) -> Result<bool, DbErr> {
|
||||
if muted_word_lists.is_empty() && muted_patterns.is_empty() {
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(check_word_mute_impl(
|
||||
&all_texts(note).await?,
|
||||
&muted_word_lists,
|
||||
&muted_patterns,
|
||||
))
|
||||
}
|
||||
}
|
67
packages/backend-rs/src/misc/convert_host.rs
Normal file
67
packages/backend-rs/src/misc/convert_host.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use crate::config::server::SERVER_CONFIG;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Idna error: {0}")]
|
||||
IdnaError(#[from] idna::Errors),
|
||||
#[error("Url parse error: {0}")]
|
||||
UrlParseError(#[from] url::ParseError),
|
||||
#[error("Hostname is missing")]
|
||||
NoHostname,
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn get_full_ap_account(username: &str, host: Option<&str>) -> Result<String, Error> {
|
||||
Ok(match host {
|
||||
Some(host) => format!("{}@{}", username, to_puny(host)?),
|
||||
None => format!("{}@{}", username, extract_host(&SERVER_CONFIG.url)?),
|
||||
})
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn is_self_host(host: Option<&str>) -> Result<bool, Error> {
|
||||
Ok(match host {
|
||||
Some(host) => extract_host(&SERVER_CONFIG.url)? == to_puny(host)?,
|
||||
None => true,
|
||||
})
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn is_same_origin(uri: &str) -> Result<bool, Error> {
|
||||
Ok(url::Url::parse(uri)?.origin().ascii_serialization() == SERVER_CONFIG.url)
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn extract_host(uri: &str) -> Result<String, Error> {
|
||||
url::Url::parse(uri)?
|
||||
.host_str()
|
||||
.ok_or(Error::NoHostname)
|
||||
.and_then(|v| Ok(to_puny(v)?))
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn to_puny(host: &str) -> Result<String, idna::Errors> {
|
||||
idna::domain_to_ascii(host)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::{extract_host, to_puny};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn extract_host_test() {
|
||||
assert_eq!(
|
||||
extract_host("https://firefish.dev/firefish/firefish.git").unwrap(),
|
||||
"firefish.dev"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_puny_test() {
|
||||
assert_eq!(
|
||||
to_puny("何もかも.owari.shop").unwrap(),
|
||||
"xn--u8jyfb5762a.owari.shop"
|
||||
);
|
||||
}
|
||||
}
|
35
packages/backend-rs/src/misc/mastodon_id.rs
Normal file
35
packages/backend-rs/src/misc/mastodon_id.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
#[crate::export]
|
||||
pub fn to_mastodon_id(firefish_id: &str) -> Option<String> {
|
||||
let decoded: [u8; 16] = basen::BASE36.decode_var_len(&firefish_id.to_ascii_lowercase())?;
|
||||
Some(basen::BASE10.encode_var_len(&decoded))
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn from_mastodon_id(mastodon_id: &str) -> Option<String> {
|
||||
let decoded: [u8; 16] = basen::BASE10.decode_var_len(mastodon_id)?;
|
||||
Some(basen::BASE36.encode_var_len(&decoded))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::{from_mastodon_id, to_mastodon_id};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn to_mastodon_id_test() {
|
||||
assert_eq!(
|
||||
to_mastodon_id("9pdqi3rjl4lxirq3").unwrap(),
|
||||
"2145531976185871567229403"
|
||||
);
|
||||
assert_eq!(to_mastodon_id("9pdqi3r*irq3"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_mastodon_id_test() {
|
||||
assert_eq!(
|
||||
from_mastodon_id("2145531976185871567229403").unwrap(),
|
||||
"9pdqi3rjl4lxirq3"
|
||||
);
|
||||
assert_eq!(from_mastodon_id("9pdqi3rjl4lxirq3"), None);
|
||||
}
|
||||
}
|
5
packages/backend-rs/src/misc/mod.rs
Normal file
5
packages/backend-rs/src/misc/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
pub mod acct;
|
||||
pub mod check_word_mute;
|
||||
pub mod convert_host;
|
||||
pub mod mastodon_id;
|
||||
pub mod nyaify;
|
97
packages/backend-rs/src/misc/nyaify.rs
Normal file
97
packages/backend-rs/src/misc/nyaify.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
use once_cell::sync::Lazy;
|
||||
use regex::{Captures, Regex};
|
||||
|
||||
#[crate::export]
|
||||
pub fn nyaify(text: &str, lang: Option<&str>) -> String {
|
||||
let mut to_return = text.to_owned();
|
||||
|
||||
{
|
||||
static RE: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"(?i-u)(non)([bcdfghjklmnpqrstvwxyz])").unwrap());
|
||||
to_return = RE
|
||||
.replace_all(&to_return, |caps: &Captures<'_>| {
|
||||
format!(
|
||||
"{}{}",
|
||||
match &caps[1] {
|
||||
"non" => "nyan",
|
||||
"Non" => "Nyan",
|
||||
"NON" => "NYAN",
|
||||
_ => &caps[1],
|
||||
},
|
||||
&caps[2]
|
||||
)
|
||||
})
|
||||
.to_string();
|
||||
}
|
||||
|
||||
{
|
||||
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"다([..。…??!!\s]|$)").unwrap());
|
||||
to_return = RE.replace_all(&to_return, r"다냥$1").to_string();
|
||||
}
|
||||
|
||||
{
|
||||
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"야([??\s]|$)").unwrap());
|
||||
to_return = RE.replace_all(&to_return, r"냥$1").to_string();
|
||||
}
|
||||
|
||||
{
|
||||
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"([나-낳])").unwrap());
|
||||
to_return = RE
|
||||
.replace_all(&to_return, |caps: &Captures<'_>| {
|
||||
format!(
|
||||
"{}",
|
||||
char::from_u32(
|
||||
caps[0].chars().next().unwrap() as u32 + 56 /* = '냐' - '나' */
|
||||
)
|
||||
.unwrap()
|
||||
)
|
||||
})
|
||||
.to_string();
|
||||
}
|
||||
|
||||
if lang.is_some() && lang.unwrap().starts_with("zh") {
|
||||
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"[妙庙描渺瞄秒苗藐廟]").unwrap());
|
||||
to_return = RE.replace_all(&to_return, "喵").to_string();
|
||||
}
|
||||
|
||||
let simple_rules = [
|
||||
("な", "にゃ"),
|
||||
("ナ", "ニャ"),
|
||||
("ナ", "ニャ"),
|
||||
("na", "nya"),
|
||||
("NA", "NYA"),
|
||||
("Na", "Nya"),
|
||||
("morning", "mornyan"),
|
||||
("Morning", "Mornyan"),
|
||||
("MORNING", "MORNYAN"),
|
||||
("everyone", "everynyan"),
|
||||
("Everyone", "Everynyan"),
|
||||
("EVERYONE", "EVERYNYAN"),
|
||||
("να", "νια"),
|
||||
("ΝΑ", "ΝΙΑ"),
|
||||
("Να", "Νια"),
|
||||
];
|
||||
|
||||
simple_rules.into_iter().for_each(|(from, to)| {
|
||||
to_return = to_return.replace(from, to);
|
||||
});
|
||||
|
||||
to_return
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::nyaify;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn can_nyaify() {
|
||||
assert_eq!(nyaify("Hello everyone!", Some("en")), "Hello everynyan!");
|
||||
assert_eq!(nyaify("Nonbinary people", None), "Nyanbinyary people");
|
||||
assert_eq!(nyaify("1分鐘是60秒", Some("zh-TW")), "1分鐘是60喵");
|
||||
assert_eq!(nyaify("1分間は60秒です", Some("ja-JP")), "1分間は60秒です");
|
||||
assert_eq!(nyaify("あなたは誰ですか", None), "あにゃたは誰ですか");
|
||||
assert_eq!(nyaify("Ναυτικός", Some("el-GR")), "Νιαυτικός");
|
||||
assert_eq!(nyaify("일어나다", None), "일어냐다냥");
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ pub enum Relation {
|
|||
from = "Column::FileId",
|
||||
to = "super::drive_file::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
on_delete = "SetNull"
|
||||
)]
|
||||
DriveFile,
|
||||
#[sea_orm(
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
use crate::impl_into_napi_error;
|
||||
|
||||
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
#[error("Failed to parse string: {0}")]
|
||||
ParseError(#[from] parse_display::ParseError),
|
||||
#[error("Failed to get database connection: {0}")]
|
||||
DbConnError(#[from] crate::database::error::Error),
|
||||
#[error("Database operation error: {0}")]
|
||||
DbOperationError(#[from] sea_orm::DbErr),
|
||||
#[error("Database error: {0}")]
|
||||
DbError(#[from] sea_orm::DbErr),
|
||||
#[error("Requested entity not found")]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
impl_into_napi_error!(Error);
|
||||
|
|
|
@ -2,18 +2,14 @@
|
|||
|
||||
use basen::BASE36;
|
||||
use cfg_if::cfg_if;
|
||||
use chrono::Utc;
|
||||
use chrono::NaiveDateTime;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::cmp;
|
||||
|
||||
use crate::impl_into_napi_error;
|
||||
|
||||
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
|
||||
#[error("ID generator has not been initialized yet")]
|
||||
pub struct ErrorUninitialized;
|
||||
|
||||
impl_into_napi_error!(ErrorUninitialized);
|
||||
|
||||
static FINGERPRINT: OnceCell<String> = OnceCell::new();
|
||||
static GENERATOR: OnceCell<cuid2::CuidConstructor> = OnceCell::new();
|
||||
|
||||
|
@ -21,7 +17,8 @@ const TIME_2000: i64 = 946_684_800_000;
|
|||
const TIMESTAMP_LENGTH: u16 = 8;
|
||||
|
||||
/// Initializes Cuid2 generator. Must be called before any [create_id].
|
||||
pub fn init_id(length: u16, fingerprint: &str) {
|
||||
#[crate::export]
|
||||
pub fn init_id_generator(length: u16, fingerprint: &str) {
|
||||
FINGERPRINT.get_or_init(move || format!("{}{}", fingerprint, cuid2::create_id()));
|
||||
GENERATOR.get_or_init(move || {
|
||||
cuid2::CuidConstructor::new()
|
||||
|
@ -33,26 +30,21 @@ pub fn init_id(length: u16, fingerprint: &str) {
|
|||
|
||||
/// Returns Cuid2 with the length specified by [init_id]. Must be called after
|
||||
/// [init_id], otherwise returns [ErrorUninitialized].
|
||||
/// The current timestamp via [chrono::Utc] is used if `date_num` is `0`.
|
||||
pub fn create_id(date_num: i64) -> Result<String, ErrorUninitialized> {
|
||||
pub fn create_id(datetime: &NaiveDateTime) -> Result<String, ErrorUninitialized> {
|
||||
match GENERATOR.get() {
|
||||
None => Err(ErrorUninitialized),
|
||||
Some(gen) => {
|
||||
let date_num = if date_num > 0 {
|
||||
date_num
|
||||
} else {
|
||||
Utc::now().timestamp_millis()
|
||||
};
|
||||
let time = cmp::max(date_num - TIME_2000, 0);
|
||||
let date_num = cmp::max(0, datetime.and_utc().timestamp_millis() - TIME_2000) as u64;
|
||||
Ok(format!(
|
||||
"{:0>8}{}",
|
||||
BASE36.encode_var_len(&(time as u64)),
|
||||
BASE36.encode_var_len(&date_num),
|
||||
gen.create_id()
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn get_timestamp(id: &str) -> i64 {
|
||||
let n: Option<u64> = BASE36.decode_var_len(&id[0..8]);
|
||||
match n {
|
||||
|
@ -63,23 +55,17 @@ pub fn get_timestamp(id: &str) -> i64 {
|
|||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "napi")] {
|
||||
use napi_derive::napi;
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
/// Calls [init_id] inside. Must be called before [native_create_id].
|
||||
#[napi]
|
||||
pub fn native_init_id_generator(length: u16, fingerprint: String) {
|
||||
init_id(length, &fingerprint);
|
||||
}
|
||||
|
||||
/// Generates
|
||||
#[napi]
|
||||
pub fn native_create_id(date_num: i64) -> String {
|
||||
create_id(date_num).unwrap()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn native_get_timestamp(id: String) -> i64 {
|
||||
get_timestamp(&id)
|
||||
/// The generated ID results in the form of `[8 chars timestamp] + [cuid2]`.
|
||||
/// The minimum and maximum lengths are 16 and 24, respectively.
|
||||
/// With the length of 16, namely 8 for cuid2, roughly 1427399 IDs are needed
|
||||
/// in the same millisecond to reach 50% chance of collision.
|
||||
///
|
||||
/// Ref: https://github.com/paralleldrive/cuid2#parameterized-length
|
||||
#[napi_derive::napi]
|
||||
pub fn gen_id(date: Option<DateTime<Utc>>) -> String {
|
||||
create_id(&date.unwrap_or_else(Utc::now).naive_utc()).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,18 +78,18 @@ mod unit_test {
|
|||
use std::thread;
|
||||
|
||||
#[test]
|
||||
fn can_create_and_decode() {
|
||||
assert_eq!(id::create_id(0), Err(id::ErrorUninitialized));
|
||||
id::init_id(16, "");
|
||||
assert_eq!(id::create_id(0).unwrap().len(), 16);
|
||||
assert_ne!(id::create_id(0).unwrap(), id::create_id(0).unwrap());
|
||||
let id1 = thread::spawn(|| id::create_id(0).unwrap());
|
||||
let id2 = thread::spawn(|| id::create_id(0).unwrap());
|
||||
fn can_create_and_decode_id() {
|
||||
let now = Utc::now().naive_utc();
|
||||
assert_eq!(id::create_id(&now), Err(id::ErrorUninitialized));
|
||||
id::init_id_generator(16, "");
|
||||
assert_eq!(id::create_id(&now).unwrap().len(), 16);
|
||||
assert_ne!(id::create_id(&now).unwrap(), id::create_id(&now).unwrap());
|
||||
let id1 = thread::spawn(move || id::create_id(&now).unwrap());
|
||||
let id2 = thread::spawn(move || id::create_id(&now).unwrap());
|
||||
assert_ne!(id1.join().unwrap(), id2.join().unwrap());
|
||||
|
||||
let now = Utc::now().timestamp_millis();
|
||||
let test_id = id::create_id(now).unwrap();
|
||||
let test_id = id::create_id(&now).unwrap();
|
||||
let timestamp = id::get_timestamp(&test_id);
|
||||
assert_eq!(now, timestamp);
|
||||
assert_eq!(now.and_utc().timestamp_millis(), timestamp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,9 @@ pub fn gen_string(length: u16) -> String {
|
|||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi")]
|
||||
#[napi_derive::napi]
|
||||
pub fn native_random_str(length: u16) -> String {
|
||||
gen_string(length)
|
||||
#[crate::export(js_name = "secureRndstr")]
|
||||
pub fn native_random_str(length: Option<u16>) -> String {
|
||||
gen_string(length.unwrap_or(32))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
"ajv": "8.12.0",
|
||||
"archiver": "7.0.1",
|
||||
"argon2": "^0.40.1",
|
||||
"aws-sdk": "2.1594.0",
|
||||
"aws-sdk": "2.1597.0",
|
||||
"axios": "^1.6.8",
|
||||
"backend-rs": "workspace:*",
|
||||
"bcryptjs": "2.4.3",
|
||||
|
@ -62,17 +62,16 @@
|
|||
"form-data": "^4.0.0",
|
||||
"got": "14.2.1",
|
||||
"gunzip-maybe": "^1.4.2",
|
||||
"happy-dom": "^14.7.0",
|
||||
"happy-dom": "^14.7.1",
|
||||
"hpagent": "1.2.0",
|
||||
"ioredis": "5.3.2",
|
||||
"ip-cidr": "4.0.0",
|
||||
"is-svg": "5.0.0",
|
||||
"js-yaml": "4.1.0",
|
||||
"json5": "2.2.3",
|
||||
"jsonld": "8.3.2",
|
||||
"jsrsasign": "11.1.0",
|
||||
"katex": "0.16.10",
|
||||
"koa": "2.15.2",
|
||||
"koa": "2.15.3",
|
||||
"koa-body": "^6.0.1",
|
||||
"koa-bodyparser": "4.4.1",
|
||||
"koa-favicon": "2.1.0",
|
||||
|
@ -92,7 +91,7 @@
|
|||
"nodemailer": "6.9.13",
|
||||
"opencc-js": "^1.0.5",
|
||||
"os-utils": "0.0.14",
|
||||
"otpauth": "^9.2.2",
|
||||
"otpauth": "^9.2.3",
|
||||
"parse5": "7.1.2",
|
||||
"pg": "8.11.5",
|
||||
"private-ip": "3.0.2",
|
||||
|
@ -104,7 +103,6 @@
|
|||
"qs": "6.12.0",
|
||||
"random-seed": "0.3.0",
|
||||
"ratelimiter": "3.4.1",
|
||||
"re2": "1.20.10",
|
||||
"redis-semaphore": "5.5.1",
|
||||
"reflect-metadata": "0.2.2",
|
||||
"rename": "1.0.4",
|
||||
|
@ -130,14 +128,13 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@swc/cli": "0.3.12",
|
||||
"@swc/core": "1.4.12",
|
||||
"@swc/core": "1.4.13",
|
||||
"@types/adm-zip": "^0.5.5",
|
||||
"@types/bcryptjs": "2.4.6",
|
||||
"@types/color-convert": "^2.0.3",
|
||||
"@types/content-disposition": "^0.5.8",
|
||||
"@types/escape-regexp": "0.0.3",
|
||||
"@types/fluent-ffmpeg": "2.1.24",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/jsonld": "1.5.13",
|
||||
"@types/jsrsasign": "10.5.13",
|
||||
"@types/katex": "0.16.7",
|
||||
|
@ -152,12 +149,12 @@
|
|||
"@types/koa__multer": "2.0.7",
|
||||
"@types/koa__router": "12.0.4",
|
||||
"@types/mocha": "10.0.6",
|
||||
"@types/node": "20.12.5",
|
||||
"@types/node": "20.12.7",
|
||||
"@types/node-fetch": "2.6.11",
|
||||
"@types/nodemailer": "6.4.14",
|
||||
"@types/oauth": "0.9.4",
|
||||
"@types/opencc-js": "^1.0.3",
|
||||
"@types/pg": "^8.11.4",
|
||||
"@types/pg": "^8.11.5",
|
||||
"@types/probe-image-size": "^7.2.4",
|
||||
"@types/pug": "2.0.10",
|
||||
"@types/punycode": "2.1.4",
|
||||
|
@ -185,7 +182,7 @@
|
|||
"ts-loader": "9.5.1",
|
||||
"ts-node": "10.9.2",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typescript": "5.4.4",
|
||||
"typescript": "5.4.5",
|
||||
"webpack": "^5.91.0",
|
||||
"ws": "8.16.0"
|
||||
}
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import cluster from "node:cluster";
|
||||
import config from "@/config/index.js";
|
||||
import { initDb } from "@/db/postgre.js";
|
||||
import { initIdGenerator } from "backend-rs";
|
||||
import os from "node:os";
|
||||
|
||||
/**
|
||||
* Init worker process
|
||||
*/
|
||||
export async function workerMain() {
|
||||
const length = Math.min(Math.max(config.cuid?.length ?? 16, 16), 24);
|
||||
const fingerprint = config.cuid?.fingerprint ?? "";
|
||||
initIdGenerator(length, fingerprint);
|
||||
|
||||
await initDb();
|
||||
|
||||
if (!process.env.mode || process.env.mode === "web") {
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
import * as fs from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname } from "node:path";
|
||||
import * as yaml from "js-yaml";
|
||||
import type { Source, Mixin } from "./types.js";
|
||||
import type { Mixin } from "./types.js";
|
||||
import { readServerConfig } from "backend-rs";
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
@ -32,7 +32,7 @@ export default function load() {
|
|||
"utf-8",
|
||||
),
|
||||
);
|
||||
const config = yaml.load(fs.readFileSync(path, "utf-8")) as Source;
|
||||
const config = readServerConfig();
|
||||
|
||||
const mixin = {} as Mixin;
|
||||
|
||||
|
|
|
@ -1,134 +1,7 @@
|
|||
/**
|
||||
* ユーザーが設定する必要のある情報
|
||||
*/
|
||||
export type Source = {
|
||||
repository_url?: string;
|
||||
feedback_url?: string;
|
||||
url: string;
|
||||
port: number;
|
||||
bind?: string;
|
||||
disableHsts?: boolean;
|
||||
db: {
|
||||
host: string;
|
||||
port: number;
|
||||
db: string;
|
||||
user: string;
|
||||
pass: string;
|
||||
disableCache?: boolean;
|
||||
extra?: { [x: string]: string };
|
||||
};
|
||||
redis: {
|
||||
host: string;
|
||||
port: number;
|
||||
family?: number;
|
||||
pass?: string;
|
||||
db?: number;
|
||||
prefix?: string;
|
||||
user?: string;
|
||||
tls?: { [y: string]: string };
|
||||
};
|
||||
cacheServer?: {
|
||||
host: string;
|
||||
port: number;
|
||||
family?: number;
|
||||
pass?: string;
|
||||
db?: number;
|
||||
prefix?: string;
|
||||
user?: string;
|
||||
tls?: { [z: string]: string };
|
||||
};
|
||||
|
||||
proxy?: string;
|
||||
proxySmtp?: string;
|
||||
proxyBypassHosts?: string[];
|
||||
|
||||
allowedPrivateNetworks?: string[];
|
||||
|
||||
maxFileSize?: number;
|
||||
|
||||
accesslog?: string;
|
||||
|
||||
clusterLimits?: {
|
||||
web?: number;
|
||||
queue?: number;
|
||||
};
|
||||
|
||||
cuid?: {
|
||||
length?: number;
|
||||
fingerprint?: string;
|
||||
};
|
||||
|
||||
outgoingAddress?: string;
|
||||
outgoingAddressFamily?: "ipv4" | "ipv6" | "dual";
|
||||
|
||||
deliverJobConcurrency?: number;
|
||||
inboxJobConcurrency?: number;
|
||||
deliverJobPerSec?: number;
|
||||
inboxJobPerSec?: number;
|
||||
deliverJobMaxAttempts?: number;
|
||||
inboxJobMaxAttempts?: number;
|
||||
|
||||
logLevel?: string[];
|
||||
|
||||
syslog: {
|
||||
host: string;
|
||||
port: number;
|
||||
};
|
||||
|
||||
mediaProxy?: string;
|
||||
proxyRemoteFiles?: boolean;
|
||||
|
||||
twa: {
|
||||
nameSpace?: string;
|
||||
packageName?: string;
|
||||
sha256CertFingerprints?: string[];
|
||||
};
|
||||
|
||||
reservedUsernames?: string[];
|
||||
|
||||
// Managed hosting stuff
|
||||
maxUserSignups?: number;
|
||||
isManagedHosting?: boolean;
|
||||
maxNoteLength?: number;
|
||||
maxCaptionLength?: number;
|
||||
deepl: {
|
||||
managed?: boolean;
|
||||
authKey?: string;
|
||||
isPro?: boolean;
|
||||
};
|
||||
libreTranslate: {
|
||||
managed?: boolean;
|
||||
apiUrl?: string;
|
||||
apiKey?: string;
|
||||
};
|
||||
email: {
|
||||
managed?: boolean;
|
||||
address?: string;
|
||||
host?: string;
|
||||
port?: number;
|
||||
user?: string;
|
||||
pass?: string;
|
||||
useImplicitSslTls?: boolean;
|
||||
};
|
||||
objectStorage: {
|
||||
managed?: boolean;
|
||||
baseUrl?: string;
|
||||
bucket?: string;
|
||||
prefix?: string;
|
||||
endpoint?: string;
|
||||
region?: string;
|
||||
accessKey?: string;
|
||||
secretKey?: string;
|
||||
useSsl?: boolean;
|
||||
connnectOverProxy?: boolean;
|
||||
setPublicReadOnUpload?: boolean;
|
||||
s3ForcePathStyle?: boolean;
|
||||
};
|
||||
summalyProxyUrl?: string;
|
||||
};
|
||||
import type { ServerConfig } from "backend-rs";
|
||||
|
||||
/**
|
||||
* Misskeyが自動的に(ユーザーが設定した情報から推論して)設定する情報
|
||||
* Firefish が自動的に(ユーザーが設定した情報から推論して)設定する情報
|
||||
*/
|
||||
export type Mixin = {
|
||||
version: string;
|
||||
|
@ -144,4 +17,4 @@ export type Mixin = {
|
|||
clientEntry: string;
|
||||
};
|
||||
|
||||
export type Config = Source & Mixin;
|
||||
export type Config = ServerConfig & Mixin;
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import type { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
import RE2 from "re2";
|
||||
|
||||
export class convertHardMutes1644010796173 implements MigrationInterface {
|
||||
async up(queryRunner: QueryRunner): Promise<void> {
|
||||
let entries = await queryRunner.query(
|
||||
|
@ -15,7 +13,7 @@ export class convertHardMutes1644010796173 implements MigrationInterface {
|
|||
if (regexp) {
|
||||
// convert regexp's
|
||||
try {
|
||||
new RE2(regexp[1], regexp[2]);
|
||||
new RegExp(regexp[1], regexp[2]);
|
||||
return `/${regexp[1]}/${regexp[2]}`;
|
||||
} catch (err) {
|
||||
// invalid regex, ignore it
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import type { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class FixChatFileConstraint1712855579316 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_535def119223ac05ad3fa9ef64b"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_535def119223ac05ad3fa9ef64b" FOREIGN KEY ("fileId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_535def119223ac05ad3fa9ef64b"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_535def119223ac05ad3fa9ef64b" FOREIGN KEY ("fileId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
export type Acct = {
|
||||
username: string;
|
||||
host: string | null;
|
||||
};
|
||||
|
||||
export function parse(acct: string): Acct {
|
||||
if (acct.startsWith("@")) acct = acct.slice(1);
|
||||
const split = acct.split("@", 2);
|
||||
return { username: split[0], host: split[1] || null };
|
||||
}
|
||||
|
||||
export function toString(acct: Acct): string {
|
||||
return acct.host == null ? acct.username : `${acct.username}@${acct.host}`;
|
||||
}
|
|
@ -3,9 +3,7 @@ import type { Note } from "@/models/entities/note.js";
|
|||
import type { User } from "@/models/entities/user.js";
|
||||
import type { UserProfile } from "@/models/entities/user-profile.js";
|
||||
import { Blockings, Followings, UserProfiles } from "@/models/index.js";
|
||||
import { getFullApAccount } from "@/misc/convert-host.js";
|
||||
import * as Acct from "@/misc/acct.js";
|
||||
import { getWordHardMute } from "@/misc/check-word-mute.js";
|
||||
import { checkWordMute, getFullApAccount, stringToAcct } from "backend-rs";
|
||||
import type { Packed } from "@/misc/schema.js";
|
||||
import { Cache } from "@/misc/cache.js";
|
||||
|
||||
|
@ -30,7 +28,7 @@ export async function checkHitAntenna(
|
|||
|
||||
if (antenna.src === "users") {
|
||||
const accts = antenna.users.map((x) => {
|
||||
const { username, host } = Acct.parse(x);
|
||||
const { username, host } = stringToAcct(x);
|
||||
return getFullApAccount(username, host).toLowerCase();
|
||||
});
|
||||
if (
|
||||
|
@ -124,7 +122,7 @@ export async function checkHitAntenna(
|
|||
mutes.mutedWords != null &&
|
||||
mutes.mutedPatterns != null &&
|
||||
antenna.userId !== note.userId &&
|
||||
(await getWordHardMute(note, mutes.mutedWords, mutes.mutedPatterns))
|
||||
(await checkWordMute(note, mutes.mutedWords, mutes.mutedPatterns))
|
||||
)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
import RE2 from "re2";
|
||||
import type { Note } from "@/models/entities/note.js";
|
||||
|
||||
type NoteLike = {
|
||||
userId: Note["userId"];
|
||||
text: Note["text"];
|
||||
files?: Note["files"];
|
||||
cw?: Note["cw"];
|
||||
reply?: NoteLike | null;
|
||||
renote?: NoteLike | null;
|
||||
};
|
||||
|
||||
function checkWordMute(
|
||||
note: NoteLike | null | undefined,
|
||||
mutedWords: string[][],
|
||||
mutedPatterns: string[],
|
||||
): boolean {
|
||||
if (note == null) return false;
|
||||
|
||||
let text = `${note.cw ?? ""} ${note.text ?? ""}`;
|
||||
if (note.files != null)
|
||||
text += ` ${note.files.map((f) => f.comment ?? "").join(" ")}`;
|
||||
text = text.trim();
|
||||
|
||||
if (text === "") return false;
|
||||
|
||||
for (const mutedWord of mutedWords) {
|
||||
// Clean up
|
||||
const keywords = mutedWord.filter((keyword) => keyword !== "");
|
||||
|
||||
if (
|
||||
keywords.length > 0 &&
|
||||
keywords.every((keyword) =>
|
||||
text.toLowerCase().includes(keyword.toLowerCase()),
|
||||
)
|
||||
)
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const mutedPattern of mutedPatterns) {
|
||||
// represents RegExp
|
||||
const regexp = mutedPattern.match(/^\/(.+)\/(.*)$/);
|
||||
|
||||
// This should never happen due to input sanitisation.
|
||||
if (!regexp) {
|
||||
console.warn(`Found invalid regex in word mutes: ${mutedPattern}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if (new RE2(regexp[1], regexp[2]).test(text)) return true;
|
||||
} catch (err) {
|
||||
// This should never happen due to input sanitisation.
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function getWordHardMute(
|
||||
note: NoteLike | null,
|
||||
mutedWords: string[][],
|
||||
mutedPatterns: string[],
|
||||
): Promise<boolean> {
|
||||
if (note == null || mutedWords == null || mutedPatterns == null) return false;
|
||||
|
||||
if (mutedWords.length > 0) {
|
||||
return (
|
||||
checkWordMute(note, mutedWords, mutedPatterns) ||
|
||||
checkWordMute(note.reply, mutedWords, mutedPatterns) ||
|
||||
checkWordMute(note.renote, mutedWords, mutedPatterns)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
import { URL } from "node:url";
|
||||
import config from "@/config/index.js";
|
||||
import { toASCII } from "punycode";
|
||||
import Logger from "@/services/logger.js";
|
||||
import { inspect } from "node:util";
|
||||
|
||||
const logger = new Logger("convert-host");
|
||||
|
||||
export function getFullApAccount(username: string, host: string | null) {
|
||||
return host
|
||||
? `${username}@${toPuny(host)}`
|
||||
: `${username}@${toPuny(config.host)}`;
|
||||
}
|
||||
|
||||
export function isSelfHost(host: string) {
|
||||
if (host == null) return true;
|
||||
return toPuny(config.host) === toPuny(host);
|
||||
}
|
||||
|
||||
export function isSameOrigin(src: unknown): boolean | null {
|
||||
if (typeof src !== "string") {
|
||||
logger.debug(`unknown origin: ${inspect(src)}`);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const u = new URL(src);
|
||||
return u.origin === config.url;
|
||||
} catch (e) {
|
||||
logger.debug(inspect(e));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function extractDbHost(uri: string) {
|
||||
const url = new URL(uri);
|
||||
return toPuny(url.hostname);
|
||||
}
|
||||
|
||||
export function toPuny(host: string) {
|
||||
return toASCII(host.toLowerCase());
|
||||
}
|
||||
|
||||
export function toPunyNullable(host: string | null | undefined): string | null {
|
||||
if (host == null) return null;
|
||||
return toASCII(host.toLowerCase());
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
import config from "@/config/index.js";
|
||||
import {
|
||||
nativeCreateId,
|
||||
nativeInitIdGenerator,
|
||||
nativeGetTimestamp,
|
||||
} from "backend-rs";
|
||||
|
||||
const length = Math.min(Math.max(config.cuid?.length ?? 16, 16), 24);
|
||||
const fingerprint = config.cuid?.fingerprint ?? "";
|
||||
nativeInitIdGenerator(length, fingerprint);
|
||||
|
||||
/**
|
||||
* The generated ID results in the form of `[8 chars timestamp] + [cuid2]`.
|
||||
* The minimum and maximum lengths are 16 and 24, respectively.
|
||||
* With the length of 16, namely 8 for cuid2, roughly 1427399 IDs are needed
|
||||
* in the same millisecond to reach 50% chance of collision.
|
||||
*
|
||||
* Ref: https://github.com/paralleldrive/cuid2#parameterized-length
|
||||
*/
|
||||
export function genId(date?: Date): string {
|
||||
return nativeCreateId(date?.getTime() ?? Date.now());
|
||||
}
|
||||
|
||||
export function getTimestamp(id: string): number {
|
||||
return nativeGetTimestamp(id);
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
export function nyaify(text: string, lang?: string): string {
|
||||
text = text
|
||||
// ja-JP
|
||||
.replaceAll("な", "にゃ")
|
||||
.replaceAll("ナ", "ニャ")
|
||||
.replaceAll("ナ", "ニャ")
|
||||
// en-US
|
||||
.replaceAll("na", "nya")
|
||||
.replaceAll("Na", "Nya")
|
||||
.replaceAll("NA", "NYA")
|
||||
.replace(/(?<=morn)ing/gi, (x) => (x === "ING" ? "YAN" : "yan"))
|
||||
.replace(/(?<=every)one/gi, (x) => (x === "ONE" ? "NYAN" : "nyan"))
|
||||
.replace(/non(?=[bcdfghjklmnpqrstvwxyz])/gi, (x) =>
|
||||
x === "NON" ? "NYAN" : "nyan",
|
||||
)
|
||||
// ko-KR
|
||||
.replace(/[나-낳]/g, (match) =>
|
||||
String.fromCharCode(
|
||||
match.charCodeAt(0)! + "냐".charCodeAt(0) - "나".charCodeAt(0),
|
||||
),
|
||||
)
|
||||
.replace(/(다$)|(다(?=\.))|(다(?= ))|(다(?=!))|(다(?=\?))/gm, "다냥")
|
||||
.replace(/(야(?=\?))|(야$)|(야(?= ))/gm, "냥")
|
||||
// el-GR
|
||||
.replaceAll("να", "νια")
|
||||
.replaceAll("ΝΑ", "ΝΙΑ")
|
||||
.replaceAll("Να", "Νια");
|
||||
|
||||
// zh-CN, zh-TW
|
||||
if (lang === "zh") text = text.replace(/(妙|庙|描|渺|瞄|秒|苗|藐|廟)/g, "喵");
|
||||
|
||||
return text;
|
||||
}
|
|
@ -3,7 +3,7 @@ import { Emojis } from "@/models/index.js";
|
|||
import type { Emoji } from "@/models/entities/emoji.js";
|
||||
import type { Note } from "@/models/entities/note.js";
|
||||
import { Cache } from "./cache.js";
|
||||
import { isSelfHost, toPunyNullable } from "./convert-host.js";
|
||||
import { isSelfHost, toPuny } from "backend-rs";
|
||||
import { decodeReaction } from "./reaction-lib.js";
|
||||
import config from "@/config/index.js";
|
||||
import { query } from "@/prelude/url.js";
|
||||
|
@ -27,7 +27,7 @@ function normalizeHost(
|
|||
noteUserHost: string | null,
|
||||
): string | null {
|
||||
// クエリに使うホスト
|
||||
let host =
|
||||
const host =
|
||||
src === "."
|
||||
? null // .はローカルホスト (ここがマッチするのはリアクションのみ)
|
||||
: src === undefined
|
||||
|
@ -36,9 +36,7 @@ function normalizeHost(
|
|||
? null // 自ホスト指定
|
||||
: src || noteUserHost; // 指定されたホスト || ノートなどの所有者のホスト (こっちがリアクションにマッチすることはない)
|
||||
|
||||
host = toPunyNullable(host);
|
||||
|
||||
return host;
|
||||
return host == null ? null : toPuny(host);
|
||||
}
|
||||
|
||||
function parseEmojiStr(emojiName: string, noteUserHost: string | null) {
|
||||
|
@ -46,11 +44,7 @@ function parseEmojiStr(emojiName: string, noteUserHost: string | null) {
|
|||
if (!match) return { name: null, host: null };
|
||||
|
||||
const name = match[1];
|
||||
|
||||
// ホスト正規化
|
||||
const host = toPunyNullable(normalizeHost(match[2], noteUserHost));
|
||||
|
||||
return { name, host };
|
||||
return { name, host: normalizeHost(match[2], noteUserHost) };
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { emojiRegex } from "./emoji-regex.js";
|
||||
import { fetchMeta } from "./fetch-meta.js";
|
||||
import { Emojis } from "@/models/index.js";
|
||||
import { toPunyNullable } from "./convert-host.js";
|
||||
import { toPuny } from "backend-rs";
|
||||
import { IsNull } from "typeorm";
|
||||
|
||||
export function convertReactions(reactions: Record<string, number>) {
|
||||
|
@ -23,7 +23,7 @@ export async function toDbReaction(
|
|||
): Promise<string> {
|
||||
if (!reaction) return (await fetchMeta()).defaultReaction;
|
||||
|
||||
reacterHost = toPunyNullable(reacterHost);
|
||||
reacterHost = reacterHost == null ? null : toPuny(reacterHost);
|
||||
|
||||
if (reaction.includes("❤") || reaction.includes("♥️")) return "❤️";
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
import { nativeRandomStr } from "backend-rs";
|
||||
|
||||
export function secureRndstr(length = 32, _ = true): string {
|
||||
return nativeRandomStr(length);
|
||||
}
|
|
@ -47,6 +47,6 @@ export class AuthSession {
|
|||
onDelete: "CASCADE",
|
||||
})
|
||||
@JoinColumn()
|
||||
public app: App;
|
||||
public app: Relation<App>;
|
||||
//#endregion
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ export class MessagingMessage {
|
|||
public group: Relation<UserGroup | null>;
|
||||
|
||||
@ManyToOne(() => DriveFile, {
|
||||
onDelete: "CASCADE", // TODO: change this to SET NULL
|
||||
onDelete: "SET NULL",
|
||||
nullable: true,
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { db } from "@/db/postgre.js";
|
||||
import { DriveFile } from "@/models/entities/drive-file.js";
|
||||
import type { User } from "@/models/entities/user.js";
|
||||
import { toPuny } from "@/misc/convert-host.js";
|
||||
import { toPuny } from "backend-rs";
|
||||
import { awaitAll } from "@/prelude/await-all.js";
|
||||
import type { Packed } from "@/misc/schema.js";
|
||||
import config from "@/config/index.js";
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
Channels,
|
||||
} from "../index.js";
|
||||
import type { Packed } from "@/misc/schema.js";
|
||||
import { nyaify } from "@/misc/nyaify.js";
|
||||
import { nyaify } from "backend-rs";
|
||||
import { awaitAll } from "@/prelude/await-all.js";
|
||||
import { convertReactions, decodeReaction } from "@/misc/reaction-lib.js";
|
||||
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as fs from "node:fs";
|
|||
import { queueLogger } from "../../logger.js";
|
||||
import { addFile } from "@/services/drive/add-file.js";
|
||||
import { format as dateFormat } from "date-fns";
|
||||
import { getFullApAccount } from "@/misc/convert-host.js";
|
||||
import { getFullApAccount } from "backend-rs";
|
||||
import { createTemp } from "@/misc/create-temp.js";
|
||||
import { Users, Blockings } from "@/models/index.js";
|
||||
import { MoreThan } from "typeorm";
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as fs from "node:fs";
|
|||
import { queueLogger } from "../../logger.js";
|
||||
import { addFile } from "@/services/drive/add-file.js";
|
||||
import { format as dateFormat } from "date-fns";
|
||||
import { getFullApAccount } from "@/misc/convert-host.js";
|
||||
import { getFullApAccount } from "backend-rs";
|
||||
import { createTemp } from "@/misc/create-temp.js";
|
||||
import { Users, Followings, Mutings } from "@/models/index.js";
|
||||
import { In, MoreThan, Not } from "typeorm";
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as fs from "node:fs";
|
|||
import { queueLogger } from "../../logger.js";
|
||||
import { addFile } from "@/services/drive/add-file.js";
|
||||
import { format as dateFormat } from "date-fns";
|
||||
import { getFullApAccount } from "@/misc/convert-host.js";
|
||||
import { getFullApAccount } from "backend-rs";
|
||||
import { createTemp } from "@/misc/create-temp.js";
|
||||
import { Users, Mutings } from "@/models/index.js";
|
||||
import { IsNull, MoreThan } from "typeorm";
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as fs from "node:fs";
|
|||
import { queueLogger } from "../../logger.js";
|
||||
import { addFile } from "@/services/drive/add-file.js";
|
||||
import { format as dateFormat } from "date-fns";
|
||||
import { getFullApAccount } from "@/misc/convert-host.js";
|
||||
import { getFullApAccount } from "backend-rs";
|
||||
import { createTemp } from "@/misc/create-temp.js";
|
||||
import { Users, UserLists, UserListJoinings } from "@/models/index.js";
|
||||
import { In } from "typeorm";
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import type Bull from "bull";
|
||||
|
||||
import { queueLogger } from "../../logger.js";
|
||||
import * as Acct from "@/misc/acct.js";
|
||||
import { isSelfHost, stringToAcct, toPuny } from "backend-rs";
|
||||
import { resolveUser } from "@/remote/resolve-user.js";
|
||||
import { downloadTextFile } from "@/misc/download-text-file.js";
|
||||
import { isSelfHost, toPuny } from "@/misc/convert-host.js";
|
||||
import { Users, DriveFiles } from "@/models/index.js";
|
||||
import type { DbUserImportJobData } from "@/queue/types.js";
|
||||
import block from "@/services/blocking/create.js";
|
||||
|
@ -42,7 +41,7 @@ export async function importBlocking(
|
|||
|
||||
try {
|
||||
const acct = line.split(",")[0].trim();
|
||||
const { username, host } = Acct.parse(acct);
|
||||
const { username, host } = stringToAcct(acct);
|
||||
|
||||
let target = isSelfHost(host!)
|
||||
? await Users.findOneBy({
|
||||
|
|
|
@ -8,7 +8,7 @@ import { downloadUrl } from "@/misc/download-url.js";
|
|||
import { DriveFiles, Emojis } from "@/models/index.js";
|
||||
import type { DbUserImportJobData } from "@/queue/types.js";
|
||||
import { addFile } from "@/services/drive/add-file.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { db } from "@/db/postgre.js";
|
||||
import probeImageSize from "probe-image-size";
|
||||
import * as path from "path";
|
||||
|
|
|
@ -9,7 +9,7 @@ import type Bull from "bull";
|
|||
import { createImportCkPostJob } from "@/queue/index.js";
|
||||
import { Notes, NoteEdits } from "@/models/index.js";
|
||||
import type { Note } from "@/models/entities/note.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
|
||||
const logger = queueLogger.createSubLogger("import-firefish-post");
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { IsNull } from "typeorm";
|
||||
import follow from "@/services/following/create.js";
|
||||
|
||||
import * as Acct from "@/misc/acct.js";
|
||||
import { isSelfHost, stringToAcct, toPuny } from "backend-rs";
|
||||
import { resolveUser } from "@/remote/resolve-user.js";
|
||||
import { downloadTextFile } from "@/misc/download-text-file.js";
|
||||
import { isSelfHost, toPuny } from "@/misc/convert-host.js";
|
||||
import { Users, DriveFiles } from "@/models/index.js";
|
||||
import type { DbUserImportJobData } from "@/queue/types.js";
|
||||
import { queueLogger } from "../../logger.js";
|
||||
|
@ -40,7 +39,7 @@ export async function importFollowing(
|
|||
if (file.type.endsWith("json")) {
|
||||
for (const acct of JSON.parse(csv)) {
|
||||
try {
|
||||
const { username, host } = Acct.parse(acct);
|
||||
const { username, host } = stringToAcct(acct);
|
||||
|
||||
let target = isSelfHost(host!)
|
||||
? await Users.findOneBy({
|
||||
|
@ -78,7 +77,7 @@ export async function importFollowing(
|
|||
|
||||
try {
|
||||
const acct = line.split(",")[0].trim();
|
||||
const { username, host } = Acct.parse(acct);
|
||||
const { username, host } = stringToAcct(acct);
|
||||
|
||||
let target = isSelfHost(host!)
|
||||
? await Users.findOneBy({
|
||||
|
|
|
@ -9,7 +9,7 @@ import { uploadFromUrl } from "@/services/drive/upload-from-url.js";
|
|||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||
import { Notes, NoteEdits } from "@/models/index.js";
|
||||
import type { Note } from "@/models/entities/note.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
|
||||
const logger = queueLogger.createSubLogger("import-masto-post");
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
import type Bull from "bull";
|
||||
|
||||
import { queueLogger } from "../../logger.js";
|
||||
import * as Acct from "@/misc/acct.js";
|
||||
import { resolveUser } from "@/remote/resolve-user.js";
|
||||
import { downloadTextFile } from "@/misc/download-text-file.js";
|
||||
import { isSelfHost, toPuny } from "@/misc/convert-host.js";
|
||||
import { Users, DriveFiles, Mutings } from "@/models/index.js";
|
||||
import type { DbUserImportJobData } from "@/queue/types.js";
|
||||
import type { User } from "@/models/entities/user.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId, isSelfHost, stringToAcct, toPuny } from "backend-rs";
|
||||
import { IsNull } from "typeorm";
|
||||
import { inspect } from "node:util";
|
||||
|
||||
|
@ -43,7 +41,7 @@ export async function importMuting(
|
|||
|
||||
try {
|
||||
const acct = line.split(",")[0].trim();
|
||||
const { username, host } = Acct.parse(acct);
|
||||
const { username, host } = stringToAcct(acct);
|
||||
|
||||
let target = isSelfHost(host!)
|
||||
? await Users.findOneBy({
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
import type Bull from "bull";
|
||||
|
||||
import { queueLogger } from "../../logger.js";
|
||||
import * as Acct from "@/misc/acct.js";
|
||||
import { resolveUser } from "@/remote/resolve-user.js";
|
||||
import { pushUserToUserList } from "@/services/user-list/push.js";
|
||||
import { downloadTextFile } from "@/misc/download-text-file.js";
|
||||
import { isSelfHost, toPuny } from "@/misc/convert-host.js";
|
||||
import {
|
||||
DriveFiles,
|
||||
Users,
|
||||
UserLists,
|
||||
UserListJoinings,
|
||||
} from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId, isSelfHost, stringToAcct, toPuny } from "backend-rs";
|
||||
import type { DbUserImportJobData } from "@/queue/types.js";
|
||||
import { IsNull } from "typeorm";
|
||||
import { inspect } from "node:util";
|
||||
|
@ -48,7 +46,7 @@ export async function importUserLists(
|
|||
|
||||
try {
|
||||
const listName = line.split(",")[0].trim();
|
||||
const { username, host } = Acct.parse(line.split(",")[1].trim());
|
||||
const { username, host } = stringToAcct(line.split(",")[1].trim());
|
||||
|
||||
let list = await UserLists.findOneBy({
|
||||
userId: user.id,
|
||||
|
|
|
@ -4,7 +4,7 @@ import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instanc
|
|||
import Logger from "@/services/logger.js";
|
||||
import { Instances } from "@/models/index.js";
|
||||
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
|
||||
import { toPuny } from "@/misc/convert-host.js";
|
||||
import { toPuny } from "backend-rs";
|
||||
import { StatusError } from "@/misc/fetch.js";
|
||||
import { shouldSkipInstance } from "@/misc/skipped-instances.js";
|
||||
import type { DeliverJobData } from "@/queue/types.js";
|
||||
|
|
|
@ -6,7 +6,7 @@ import Logger from "@/services/logger.js";
|
|||
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
|
||||
import { Instances } from "@/models/index.js";
|
||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||
import { toPuny, extractDbHost } from "@/misc/convert-host.js";
|
||||
import { toPuny, extractHost } from "backend-rs";
|
||||
import { getApId } from "@/remote/activitypub/type.js";
|
||||
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
|
||||
import type { InboxJobData } from "../types.js";
|
||||
|
@ -157,7 +157,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
|
|||
}
|
||||
|
||||
// ブロックしてたら中断
|
||||
const ldHost = extractDbHost(authUser.user.uri);
|
||||
const ldHost = extractHost(authUser.user.uri);
|
||||
if (await shouldBlockInstance(ldHost, meta)) {
|
||||
return `Blocked request: ${ldHost}`;
|
||||
}
|
||||
|
@ -168,8 +168,8 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
|
|||
|
||||
// activity.idがあればホストが署名者のホストであることを確認する
|
||||
if (typeof activity.id === "string") {
|
||||
const signerHost = extractDbHost(authUser.user.uri!);
|
||||
const activityIdHost = extractDbHost(activity.id);
|
||||
const signerHost = extractHost(authUser.user.uri!);
|
||||
const activityIdHost = extractHost(activity.id);
|
||||
if (signerHost !== activityIdHost) {
|
||||
return `skip: signerHost(${signerHost}) !== activity.id host(${activityIdHost}`;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { URL } from "url";
|
|||
import httpSignature, { IParsedSignature } from "@peertube/http-signature";
|
||||
import config from "@/config/index.js";
|
||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||
import { toPuny } from "@/misc/convert-host.js";
|
||||
import { toPuny } from "backend-rs";
|
||||
import DbResolver from "@/remote/activitypub/db-resolver.js";
|
||||
import { getApId } from "@/remote/activitypub/type.js";
|
||||
import { shouldBlockInstance } from "@/misc/should-block-instance.js";
|
||||
|
|
|
@ -5,7 +5,7 @@ import type { IAnnounce } from "../../type.js";
|
|||
import { getApId } from "../../type.js";
|
||||
import { fetchNote, resolveNote } from "../../models/note.js";
|
||||
import { apLogger } from "../../logger.js";
|
||||
import { extractDbHost } from "@/misc/convert-host.js";
|
||||
import { extractHost } from "backend-rs";
|
||||
import { getApLock } from "@/misc/app-lock.js";
|
||||
import { parseAudience } from "../../audience.js";
|
||||
import { StatusError } from "@/misc/fetch.js";
|
||||
|
@ -31,7 +31,7 @@ export default async function (
|
|||
}
|
||||
|
||||
// Interrupt if you block the announcement destination
|
||||
if (await shouldBlockInstance(extractDbHost(uri))) return;
|
||||
if (await shouldBlockInstance(extractHost(uri))) return;
|
||||
|
||||
const lock = await getApLock(uri);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { createNote, fetchNote } from "../../models/note.js";
|
|||
import type { IObject, ICreate } from "../../type.js";
|
||||
import { getApId } from "../../type.js";
|
||||
import { getApLock } from "@/misc/app-lock.js";
|
||||
import { extractDbHost } from "@/misc/convert-host.js";
|
||||
import { extractHost } from "backend-rs";
|
||||
import { StatusError } from "@/misc/fetch.js";
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@ export default async function (
|
|||
}
|
||||
|
||||
if (typeof note.id === "string") {
|
||||
if (extractDbHost(actor.uri) !== extractDbHost(note.id)) {
|
||||
if (extractHost(actor.uri) !== extractHost(note.id)) {
|
||||
return "skip: host in actor.uri !== note.id";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { IFlag } from "../../type.js";
|
|||
import { getApIds } from "../../type.js";
|
||||
import { AbuseUserReports, Users } from "@/models/index.js";
|
||||
import { In } from "typeorm";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
|
||||
export default async (
|
||||
actor: CacheableRemoteUser,
|
||||
|
|
|
@ -38,7 +38,7 @@ import block from "./block/index.js";
|
|||
import flag from "./flag/index.js";
|
||||
import move from "./move/index.js";
|
||||
import type { IObject, IActivity } from "../type.js";
|
||||
import { extractDbHost } from "@/misc/convert-host.js";
|
||||
import { extractHost } from "backend-rs";
|
||||
import { shouldBlockInstance } from "@/misc/should-block-instance.js";
|
||||
import { inspect } from "node:util";
|
||||
|
||||
|
@ -70,7 +70,7 @@ async function performOneActivity(
|
|||
if (actor.isSuspended) return;
|
||||
|
||||
if (typeof activity.id !== "undefined") {
|
||||
const host = extractDbHost(getApId(activity));
|
||||
const host = extractHost(getApId(activity));
|
||||
if (await shouldBlockInstance(host)) return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { CacheableRemoteUser } from "@/models/entities/user.js";
|
||||
import type { IRead } from "../type.js";
|
||||
import { getApId } from "../type.js";
|
||||
import { isSelfHost, extractDbHost } from "@/misc/convert-host.js";
|
||||
import { isSelfHost, extractHost } from "backend-rs";
|
||||
import { MessagingMessages } from "@/models/index.js";
|
||||
import { readUserMessagingMessage } from "@/server/api/common/read-messaging-message.js";
|
||||
|
||||
|
@ -11,7 +11,7 @@ export const performReadActivity = async (
|
|||
): Promise<string> => {
|
||||
const id = await getApId(activity.object);
|
||||
|
||||
if (!isSelfHost(extractDbHost(id))) {
|
||||
if (!isSelfHost(extractHost(id))) {
|
||||
return `skip: Read to foreign host (${id})`;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import { extractPollFromQuestion } from "./question.js";
|
|||
import vote from "@/services/note/polls/vote.js";
|
||||
import { apLogger } from "../logger.js";
|
||||
import { DriveFile } from "@/models/entities/drive-file.js";
|
||||
import { extractDbHost, isSameOrigin, toPuny } from "@/misc/convert-host.js";
|
||||
import { extractHost, isSameOrigin, toPuny } from "backend-rs";
|
||||
import {
|
||||
Emojis,
|
||||
Polls,
|
||||
|
@ -33,7 +33,7 @@ import {
|
|||
getApType,
|
||||
} from "../type.js";
|
||||
import type { Emoji } from "@/models/entities/emoji.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { getApLock } from "@/misc/app-lock.js";
|
||||
import { createMessage } from "@/services/messages/create.js";
|
||||
import { parseAudience } from "../audience.js";
|
||||
|
@ -54,7 +54,7 @@ import { inspect } from "node:util";
|
|||
const logger = apLogger;
|
||||
|
||||
export function validateNote(object: any, uri: string) {
|
||||
const expectHost = extractDbHost(uri);
|
||||
const expectHost = extractHost(uri);
|
||||
|
||||
if (object == null) {
|
||||
return new Error("invalid Note: object is null");
|
||||
|
@ -64,9 +64,9 @@ export function validateNote(object: any, uri: string) {
|
|||
return new Error(`invalid Note: invalid object type ${getApType(object)}`);
|
||||
}
|
||||
|
||||
if (object.id && extractDbHost(object.id) !== expectHost) {
|
||||
if (object.id && extractHost(object.id) !== expectHost) {
|
||||
return new Error(
|
||||
`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(
|
||||
`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractHost(
|
||||
object.id,
|
||||
)}`,
|
||||
);
|
||||
|
@ -74,10 +74,10 @@ export function validateNote(object: any, uri: string) {
|
|||
|
||||
if (
|
||||
object.attributedTo &&
|
||||
extractDbHost(getOneApId(object.attributedTo)) !== expectHost
|
||||
extractHost(getOneApId(object.attributedTo)) !== expectHost
|
||||
) {
|
||||
return new Error(
|
||||
`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(
|
||||
`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractHost(
|
||||
object.attributedTo,
|
||||
)}`,
|
||||
);
|
||||
|
@ -420,11 +420,11 @@ export async function resolveNote(
|
|||
if (uri == null) throw new Error("missing uri");
|
||||
|
||||
// Abort if origin host is blocked
|
||||
if (await shouldBlockInstance(extractDbHost(uri)))
|
||||
if (await shouldBlockInstance(extractHost(uri)))
|
||||
throw new StatusError(
|
||||
"host blocked",
|
||||
451,
|
||||
`host ${extractDbHost(uri)} is blocked`,
|
||||
`host ${extractHost(uri)} is blocked`,
|
||||
);
|
||||
|
||||
const lock = await getApLock(uri);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { URL } from "node:url";
|
||||
import promiseLimit from "promise-limit";
|
||||
|
||||
import config from "@/config/index.js";
|
||||
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
|
||||
import type { Note } from "@/models/entities/note.js";
|
||||
import { updateUsertags } from "@/services/update-hashtag.js";
|
||||
|
@ -16,10 +15,10 @@ import type { IRemoteUser, CacheableUser } from "@/models/entities/user.js";
|
|||
import { User } from "@/models/entities/user.js";
|
||||
import type { Emoji } from "@/models/entities/emoji.js";
|
||||
import { UserNotePining } from "@/models/entities/user-note-pining.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { UserPublickey } from "@/models/entities/user-publickey.js";
|
||||
import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
|
||||
import { isSameOrigin, toPuny } from "@/misc/convert-host.js";
|
||||
import { isSameOrigin, toPuny } from "backend-rs";
|
||||
import { UserProfile } from "@/models/entities/user-profile.js";
|
||||
import { toArray } from "@/prelude/array.js";
|
||||
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
|
||||
|
@ -164,8 +163,6 @@ export async function createPerson(
|
|||
uri: string,
|
||||
resolver?: Resolver,
|
||||
): Promise<User> {
|
||||
if (typeof uri !== "string") throw new Error("uri is not string");
|
||||
|
||||
if (isSameOrigin(uri)) {
|
||||
throw new StatusError(
|
||||
"cannot resolve local user",
|
||||
|
|
|
@ -4,7 +4,7 @@ import { getApId, isQuestion } from "../type.js";
|
|||
import { apLogger } from "../logger.js";
|
||||
import { Notes, Polls } from "@/models/index.js";
|
||||
import type { IPoll } from "@/models/entities/poll.js";
|
||||
import { isSameOrigin } from "@/misc/convert-host.js";
|
||||
import { isSameOrigin } from "backend-rs";
|
||||
|
||||
export async function extractPollFromQuestion(
|
||||
source: string | IObject,
|
||||
|
|
|
@ -2,7 +2,7 @@ import config from "@/config/index.js";
|
|||
import type { ILocalUser } from "@/models/entities/user.js";
|
||||
import { getInstanceActor } from "@/services/instance-actor.js";
|
||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||
import { extractDbHost, isSelfHost } from "@/misc/convert-host.js";
|
||||
import { extractHost, isSelfHost } from "backend-rs";
|
||||
import { apGet } from "./request.js";
|
||||
import type { IObject, ICollection, IOrderedCollection } from "./type.js";
|
||||
import { isCollectionOrOrderedCollection, getApId } from "./type.js";
|
||||
|
@ -68,7 +68,7 @@ export default class Resolver {
|
|||
if (typeof value !== "string") {
|
||||
apLogger.debug("Object to resolve is not a string");
|
||||
if (typeof value.id !== "undefined") {
|
||||
const host = extractDbHost(getApId(value));
|
||||
const host = extractHost(getApId(value));
|
||||
if (await shouldBlockInstance(host)) {
|
||||
throw new Error("instance is blocked");
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ export default class Resolver {
|
|||
}
|
||||
this.history.add(value);
|
||||
|
||||
const host = extractDbHost(value);
|
||||
const host = extractHost(value);
|
||||
if (isSelfHost(host)) {
|
||||
return await this.resolveLocal(value);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { IsNull } from "typeorm";
|
|||
import config from "@/config/index.js";
|
||||
import type { User, IRemoteUser } from "@/models/entities/user.js";
|
||||
import { Users } from "@/models/index.js";
|
||||
import { toPuny } from "@/misc/convert-host.js";
|
||||
import { toPuny } from "backend-rs";
|
||||
import webFinger from "./webfinger.js";
|
||||
import { createPerson, updatePerson } from "./activitypub/models/person.js";
|
||||
import { remoteLogger } from "./logger.js";
|
||||
|
|
|
@ -9,7 +9,7 @@ import renderKey from "@/remote/activitypub/renderer/key.js";
|
|||
import { renderPerson } from "@/remote/activitypub/renderer/person.js";
|
||||
import renderEmoji from "@/remote/activitypub/renderer/emoji.js";
|
||||
import { inbox as processInbox } from "@/queue/index.js";
|
||||
import { isSelfHost } from "@/misc/convert-host.js";
|
||||
import { isSelfHost } from "backend-rs";
|
||||
import {
|
||||
Notes,
|
||||
Users,
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import { secureRndstr } from "@/misc/secure-rndstr.js";
|
||||
import { secureRndstr } from "backend-rs";
|
||||
|
||||
export default () => secureRndstr(16, true);
|
||||
export default () => secureRndstr(16);
|
||||
|
|
|
@ -3,7 +3,7 @@ import type Koa from "koa";
|
|||
import config from "@/config/index.js";
|
||||
import type { ILocalUser } from "@/models/entities/user.js";
|
||||
import { Signins } from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { publishMainStream } from "@/services/stream.js";
|
||||
|
||||
export default function (ctx: Koa.Context, user: ILocalUser, redirect = false) {
|
||||
|
|
|
@ -4,8 +4,7 @@ import { User } from "@/models/entities/user.js";
|
|||
import { Users, UsedUsernames } from "@/models/index.js";
|
||||
import { UserProfile } from "@/models/entities/user-profile.js";
|
||||
import { IsNull } from "typeorm";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { toPunyNullable } from "@/misc/convert-host.js";
|
||||
import { genId, toPuny } from "backend-rs";
|
||||
import { UserKeypair } from "@/models/entities/user-keypair.js";
|
||||
import { UsedUsername } from "@/models/entities/used-username.js";
|
||||
import { db } from "@/db/postgre.js";
|
||||
|
@ -100,7 +99,7 @@ export async function signup(opts: {
|
|||
createdAt: new Date(),
|
||||
username: username,
|
||||
usernameLower: username.toLowerCase(),
|
||||
host: toPunyNullable(host),
|
||||
host: host == null ? null : toPuny(host),
|
||||
token: secret,
|
||||
isAdmin:
|
||||
(await Users.countBy({
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Ads } from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["admin"],
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Announcements } from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["admin"],
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Emojis, DriveFiles } from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { insertModerationLog } from "@/services/insert-moderation-log.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import rndstr from "rndstr";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Emojis } from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||
import { uploadFromUrl } from "@/services/drive/upload-from-url.js";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Emojis } from "@/models/index.js";
|
||||
import { toPuny } from "@/misc/convert-host.js";
|
||||
import { toPuny } from "backend-rs";
|
||||
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
||||
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Instances } from "@/models/index.js";
|
||||
import { toPuny } from "@/misc/convert-host.js";
|
||||
import { toPuny } from "backend-rs";
|
||||
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
|
||||
|
||||
export const meta = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Instances } from "@/models/index.js";
|
||||
import { toPuny } from "@/misc/convert-host.js";
|
||||
import { toPuny } from "backend-rs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["admin"],
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import rndstr from "rndstr";
|
||||
import define from "@/server/api/define.js";
|
||||
import { RegistrationTickets } from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["admin"],
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { Antennas, UserLists, UserGroupJoinings } from "@/models/index.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { publishInternalEvent } from "@/services/stream.js";
|
||||
|
|
|
@ -2,7 +2,7 @@ import define from "@/server/api/define.js";
|
|||
import readNote from "@/services/note/read.js";
|
||||
import { Antennas, Notes } from "@/models/index.js";
|
||||
import { redisClient } from "@/db/redis.js";
|
||||
import { getTimestamp } from "@/misc/gen-id.js";
|
||||
import { getTimestamp } from "backend-rs";
|
||||
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
||||
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
|
||||
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
|
||||
|
|
|
@ -4,7 +4,7 @@ import { createNote } from "@/remote/activitypub/models/note.js";
|
|||
import DbResolver from "@/remote/activitypub/db-resolver.js";
|
||||
import Resolver from "@/remote/activitypub/resolver.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { extractDbHost } from "@/misc/convert-host.js";
|
||||
import { extractHost } from "backend-rs";
|
||||
import { Users, Notes } from "@/models/index.js";
|
||||
import type { Note } from "@/models/entities/note.js";
|
||||
import type { CacheableLocalUser, User } from "@/models/entities/user.js";
|
||||
|
@ -101,7 +101,7 @@ async function fetchAny(
|
|||
me: CacheableLocalUser | null | undefined,
|
||||
): Promise<SchemaType<(typeof meta)["res"]> | null> {
|
||||
// Wait if blocked.
|
||||
if (await shouldBlockInstance(extractDbHost(uri))) return null;
|
||||
if (await shouldBlockInstance(extractHost(uri))) return null;
|
||||
|
||||
const dbResolver = new DbResolver();
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Apps } from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId, secureRndstr } from "backend-rs";
|
||||
import { unique } from "@/prelude/array.js";
|
||||
import { secureRndstr } from "@/misc/secure-rndstr.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["app"],
|
||||
|
@ -41,7 +40,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
includeSecret: true,
|
||||
});
|
||||
// Generate secret
|
||||
const secret = secureRndstr(32, true);
|
||||
const secret = secureRndstr(32);
|
||||
|
||||
// for backward compatibility
|
||||
const permission = unique(
|
||||
|
|
|
@ -2,8 +2,7 @@ import * as crypto from "node:crypto";
|
|||
import define from "@/server/api/define.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { AuthSessions, AccessTokens, Apps } from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { secureRndstr } from "@/misc/secure-rndstr.js";
|
||||
import { genId, secureRndstr } from "backend-rs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["auth"],
|
||||
|
@ -38,7 +37,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
}
|
||||
|
||||
// Generate access token
|
||||
const accessToken = secureRndstr(32, true);
|
||||
const accessToken = secureRndstr(32);
|
||||
|
||||
// Fetch exist access token
|
||||
const exist = await AccessTokens.exist({
|
||||
|
|
|
@ -3,7 +3,7 @@ import config from "@/config/index.js";
|
|||
import define from "@/server/api/define.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { Apps, AuthSessions } from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["auth"],
|
||||
|
|
|
@ -2,7 +2,7 @@ import define from "@/server/api/define.js";
|
|||
import { ApiError } from "@/server/api/error.js";
|
||||
import { Channels, DriveFiles } from "@/models/index.js";
|
||||
import type { Channel } from "@/models/entities/channel.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["channels"],
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { Channels, ChannelFollowings } from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { publishUserEvent } from "@/services/stream.js";
|
||||
|
||||
export const meta = {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { ClipNotes, Clips } from "@/models/index.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { getNote } from "@/server/api/common/getters.js";
|
||||
|
||||
export const meta = {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { Clips } from "@/models/index.js";
|
||||
|
||||
export const meta = {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue