Merge branch 'develop' of https://firefish.dev/firefish/firefish into refactor/types

This commit is contained in:
Lhcfl 2024-04-13 15:39:44 +08:00
commit f95e2d1c51
188 changed files with 3095 additions and 1557 deletions

View File

@ -10,7 +10,7 @@ node_modules
report.*.json
# Rust
packages/backend-rs/target
/target
# Coverage
coverage

3
.gitignore vendored
View File

@ -14,6 +14,9 @@ packages/backend/.idea/vcs.xml
node_modules
report.*.json
# Cargo
/target
# Cypress
cypress/screenshots
cypress/videos

View File

@ -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.

View File

@ -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.

View File

@ -1,5 +0,0 @@
{
"rust-analyzer.linkedProjects": [
"packages/backend-rs/Cargo.toml"
]
}

View File

@ -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
View 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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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"
}
}

View File

@ -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 }

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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"

View File

@ -0,0 +1 @@
pub mod server;

View 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);

View File

@ -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);

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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())
}
}
};
}

View File

@ -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))
}

View 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);
}
}

View 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,
))
}
}

View 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"
);
}
}

View 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);
}
}

View File

@ -0,0 +1,5 @@
pub mod acct;
pub mod check_word_mute;
pub mod convert_host;
pub mod mastodon_id;
pub mod nyaify;

View 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), "일어냐다냥");
}
}

View File

@ -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(

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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)]

View File

@ -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"
}

View File

@ -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") {

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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`,
);
}
}

View File

@ -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}`;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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());
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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) };
}
/**

View File

@ -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 "❤️";

View File

@ -1,5 +0,0 @@
import { nativeRandomStr } from "backend-rs";
export function secureRndstr(length = 32, _ = true): string {
return nativeRandomStr(length);
}

View File

@ -47,6 +47,6 @@ export class AuthSession {
onDelete: "CASCADE",
})
@JoinColumn()
public app: App;
public app: Relation<App>;
//#endregion
}

View File

@ -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()

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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({

View File

@ -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";

View File

@ -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");

View File

@ -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({

View File

@ -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");

View File

@ -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({

View File

@ -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,

View File

@ -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";

View File

@ -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}`;
}

View File

@ -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";

View File

@ -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);

View File

@ -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";
}
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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})`;
}

View File

@ -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);

View File

@ -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",

View File

@ -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,

View File

@ -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);
}

View File

@ -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";

View File

@ -9,7 +9,7 @@ import renderKey from "@/remote/activitypub/renderer/key.js";
import { renderPerson } from "@/remote/activitypub/renderer/person.js";
import renderEmoji from "@/remote/activitypub/renderer/emoji.js";
import { inbox as processInbox } from "@/queue/index.js";
import { isSelfHost } from "@/misc/convert-host.js";
import { isSelfHost } from "backend-rs";
import {
Notes,
Users,

View File

@ -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);

View File

@ -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) {

View File

@ -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({

View File

@ -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"],

View File

@ -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"],

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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 = {

View File

@ -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"],

View File

@ -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"],

View File

@ -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";

View File

@ -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";

View File

@ -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();

View File

@ -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(

View File

@ -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({

View File

@ -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"],

View File

@ -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"],

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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