Compare commits
41 Commits
0871a7c16f
...
285b681adb
Author | SHA1 | Date |
---|---|---|
laozhoubuluo | 285b681adb | |
naskya | 4005307cbe | |
naskya | 2535fed09a | |
CI | 7e3b46f504 | |
CI | 32c00d3014 | |
naskya | aa78efd20e | |
naskya | dbcecf6966 | |
naskya | 3f22cdbf09 | |
naskya | 134bbb48ab | |
naskya | 8174d646f9 | |
naskya | 84596c8313 | |
naskya | 9dbf6d07c4 | |
naskya | 2f4c30176d | |
naskya | c73ce2ff39 | |
naskya | 797c768f24 | |
naskya | 945465cc5e | |
naskya | fb232b4797 | |
naskya | 3661c4da74 | |
CI | 654e71e43a | |
CI | 819cb9a824 | |
Lhcfl | c18987e3b4 | |
naskya | 2613dfd952 | |
naskya | dfc8763f0c | |
CI | 3d5003acc2 | |
naskya | b8e5b8616a | |
naskya | 3a7c5fcdbb | |
naskya | 1c0d2e43b4 | |
naskya | d9ff9101a1 | |
naskya | 58268a2c6e | |
sup39 | c4a7cd6029 | |
sup39 | 2a8193fa6c | |
sup39 | ada5ff7e75 | |
naskya | 7dd3b8ec5a | |
naskya | 88890153a1 | |
naskya | 23f47aac38 | |
naskya | abc9d58f7c | |
Daniel Smith | ab3ca2a20b | |
naskya | fdc77b74ae | |
naskya | 61562a0943 | |
CI | a298302f76 | |
老周部落 | 469ca68e2e |
|
@ -59,7 +59,7 @@ test:build:
|
|||
rules:
|
||||
- if: $TEST == 'false'
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop'
|
||||
changes:
|
||||
paths:
|
||||
- packages/backend-rs/**/*
|
||||
|
@ -84,7 +84,7 @@ test:build:backend_ts_only:
|
|||
rules:
|
||||
- if: $TEST == 'false'
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop'
|
||||
changes:
|
||||
paths:
|
||||
- packages/backend-rs/**/*
|
||||
|
@ -94,7 +94,7 @@ test:build:backend_ts_only:
|
|||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop'
|
||||
changes:
|
||||
paths:
|
||||
- packages/backend/**/*
|
||||
|
@ -124,7 +124,7 @@ test:build:client_only:
|
|||
rules:
|
||||
- if: $TEST == 'false'
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop'
|
||||
changes:
|
||||
paths:
|
||||
- packages/backend-rs/**/*
|
||||
|
@ -134,7 +134,7 @@ test:build:client_only:
|
|||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop'
|
||||
changes:
|
||||
paths:
|
||||
- packages/client/**/*
|
||||
|
@ -157,7 +157,7 @@ test:build:client_only:
|
|||
|
||||
build:container:
|
||||
stage: build
|
||||
image: docker.io/debian:bookworm-slim
|
||||
image: quay.io/buildah/stable:latest
|
||||
services: []
|
||||
rules:
|
||||
- if: $BUILD == 'false'
|
||||
|
@ -181,20 +181,17 @@ build:container:
|
|||
optional: true
|
||||
- job: test:build:client_only
|
||||
optional: true
|
||||
variables:
|
||||
STORAGE_DRIVER: overlay2
|
||||
before_script:
|
||||
- apt-get update && apt-get -y upgrade
|
||||
- apt-get install -y --no-install-recommends buildah ca-certificates fuse-overlayfs
|
||||
- buildah login --username "${CI_REGISTRY_USER}" --password "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
|
||||
- buildah version
|
||||
- buildah prune --all --force || true
|
||||
- echo "${CI_REGISTRY_PASSWORD}" | buildah login --username "${CI_REGISTRY_USER}" --password-stdin "${CI_REGISTRY}"
|
||||
- export IMAGE_TAG="${CI_REGISTRY}/${CI_PROJECT_PATH}/develop:not-for-production"
|
||||
- export IMAGE_CACHE="${CI_REGISTRY}/${CI_PROJECT_PATH}/develop/cache"
|
||||
script:
|
||||
- |-
|
||||
buildah build \
|
||||
--isolation chroot \
|
||||
--device /dev/fuse:rw \
|
||||
--security-opt seccomp=unconfined \
|
||||
--security-opt apparmor=unconfined \
|
||||
--cap-add all \
|
||||
--platform linux/amd64 \
|
||||
--layers \
|
||||
--cache-to "${IMAGE_CACHE}" \
|
||||
|
@ -209,23 +206,17 @@ cargo:test:
|
|||
rules:
|
||||
- if: $TEST == 'false'
|
||||
when: never
|
||||
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'main'
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop'
|
||||
changes:
|
||||
paths:
|
||||
- packages/backend-rs/**/*
|
||||
- packages/macro-rs/**/*
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- package.json
|
||||
when: always
|
||||
script:
|
||||
- curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C /usr/local/cargo/bin
|
||||
- pnpm install --frozen-lockfile
|
||||
- mkdir -p packages/backend-rs/built
|
||||
- cp packages/backend-rs/index.js packages/backend-rs/built/index.js
|
||||
- cp packages/backend-rs/index.d.ts packages/backend-rs/built/index.d.ts
|
||||
- pnpm --filter='!backend-rs' run build:debug
|
||||
- cargo test --doc
|
||||
- cargo nextest run
|
||||
|
||||
|
@ -234,9 +225,7 @@ cargo:clippy:
|
|||
rules:
|
||||
- if: $TEST == 'false'
|
||||
when: never
|
||||
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'main'
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
|
||||
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop'
|
||||
changes:
|
||||
paths:
|
||||
- packages/backend-rs/**/*
|
||||
|
|
4
COPYING
4
COPYING
|
@ -30,6 +30,10 @@ Chiptune2.js by Simon Gündling
|
|||
License: MIT
|
||||
https://github.com/deskjet/chiptune2.js#license
|
||||
|
||||
bb8-redis by Kyle Huey
|
||||
License: MIT
|
||||
https://github.com/djc/bb8/blob/62597aa45ac1746780b08cb6a68cf7d65452a23a/LICENSE
|
||||
|
||||
Licenses for all softwares and software libraries installed via the Node Package Manager ("npm") can be found by running the following shell command in the root directory of this repository:
|
||||
|
||||
pnpm licenses list
|
||||
|
|
|
@ -85,9 +85,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.84"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18b8795de6d09abb2b178fa5a9e3bb10da935750f33449a132b328b9391b2c6a"
|
||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
|
@ -211,7 +211,9 @@ name = "backend-rs"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"async-trait",
|
||||
"basen",
|
||||
"bb8",
|
||||
"bcrypt",
|
||||
"chrono",
|
||||
"cuid2",
|
||||
|
@ -219,7 +221,7 @@ dependencies = [
|
|||
"idna",
|
||||
"image",
|
||||
"isahc",
|
||||
"macro_rs",
|
||||
"macro-rs",
|
||||
"napi",
|
||||
"napi-build",
|
||||
"napi-derive",
|
||||
|
@ -239,6 +241,7 @@ dependencies = [
|
|||
"sysinfo",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-test",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
|
@ -297,6 +300,19 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dbe4bb73fd931c4d1aaf53b35d1286c8a948ad00ec92c8e3c856f15fd027f43"
|
||||
|
||||
[[package]]
|
||||
name = "bb8"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df7c2093d15d6a1d33b1f972e1c5ea3177748742b97a5f392aa83a65262c6780"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"parking_lot",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "0.15.1"
|
||||
|
@ -484,9 +500,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.97"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
|
||||
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
|
@ -564,7 +580,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1203,9 +1223,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
|
@ -1622,9 +1642,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
|
@ -1718,13 +1738,15 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "macro_rs"
|
||||
name = "macro-rs"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"napi",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 2.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
@ -1790,14 +1812,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "napi"
|
||||
version = "2.16.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfc300228808a0e6aea5a58115c82889240bcf8dab16fc25ad675b33e454b368"
|
||||
version = "3.0.0-alpha.2"
|
||||
source = "git+https://github.com/napi-rs/napi-rs.git?rev=ca2cd5c35a0c39ec4a94e93c6c5695b681046df2#ca2cd5c35a0c39ec4a94e93c6c5695b681046df2"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"chrono",
|
||||
"ctor",
|
||||
"napi-derive",
|
||||
"napi-sys",
|
||||
"once_cell",
|
||||
"serde",
|
||||
|
@ -1813,9 +1833,9 @@ checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a"
|
|||
|
||||
[[package]]
|
||||
name = "napi-derive"
|
||||
version = "2.16.4"
|
||||
version = "2.16.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb613535cde46cff231e53cd819c1694a32d48946bc2dda6b41174ace52ac08"
|
||||
checksum = "e0e034ddf6155192cf83f267ede763fe6c164dfa9971585436b16173718d94c4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"convert_case",
|
||||
|
@ -1827,9 +1847,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "napi-derive-backend"
|
||||
version = "1.0.66"
|
||||
version = "1.0.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da041b19246ab4240998774e987fd9a7d92cc7406b91b5eddb6691e81feac044"
|
||||
checksum = "bff2c00437f3b3266391eb5e6aa25d0029187daf5caf05b8e3271468fb5ae73e"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"once_cell",
|
||||
|
@ -1843,8 +1863,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "napi-sys"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3"
|
||||
source = "git+https://github.com/napi-rs/napi-rs.git?rev=ca2cd5c35a0c39ec4a94e93c6c5695b681046df2#ca2cd5c35a0c39ec4a94e93c6c5695b681046df2"
|
||||
dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
@ -2618,12 +2637,16 @@ version = "0.25.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6472825949c09872e8f2c50bde59fcefc17748b6be5c90fd67cd8b4daca73bfd"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"combine",
|
||||
"futures-util",
|
||||
"itoa",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"ryu",
|
||||
"sha1_smol",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
@ -3100,12 +3123,6 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
|
@ -3767,6 +3784,32 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-test"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.13"
|
||||
|
@ -4082,7 +4125,7 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
|||
[[package]]
|
||||
name = "web-push"
|
||||
version = "0.10.1"
|
||||
source = "git+https://github.com/pimeys/rust-web-push?rev=40febe4085e3cef9cdfd539c315e3e945aba0656#40febe4085e3cef9cdfd539c315e3e945aba0656"
|
||||
source = "git+https://github.com/pimeys/rust-web-push.git?rev=40febe4085e3cef9cdfd539c315e3e945aba0656#40febe4085e3cef9cdfd539c315e3e945aba0656"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.13.1",
|
||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -3,14 +3,16 @@ members = ["packages/backend-rs", "packages/macro-rs"]
|
|||
resolver = "2"
|
||||
|
||||
[workspace.dependencies]
|
||||
macro_rs = { path = "packages/macro-rs" }
|
||||
macro-rs = { path = "packages/macro-rs" }
|
||||
|
||||
napi = { version = "2.16.6", default-features = false }
|
||||
napi-derive = "2.16.4"
|
||||
napi = { git = "https://github.com/napi-rs/napi-rs.git", rev = "ca2cd5c35a0c39ec4a94e93c6c5695b681046df2", default-features = false }
|
||||
napi-derive = "2.16.5"
|
||||
napi-build = "2.1.3"
|
||||
|
||||
argon2 = "0.5.3"
|
||||
async-trait = "0.1.80"
|
||||
basen = "0.1.0"
|
||||
bb8 = "0.8.3"
|
||||
bcrypt = "0.15.1"
|
||||
chrono = "0.4.38"
|
||||
convert_case = "0.6.0"
|
||||
|
@ -26,7 +28,7 @@ pretty_assertions = "1.4.0"
|
|||
proc-macro2 = "1.0.82"
|
||||
quote = "1.0.36"
|
||||
rand = "0.8.5"
|
||||
redis = "0.25.3"
|
||||
redis = { version = "0.25.3", default-features = false }
|
||||
regex = "1.10.4"
|
||||
rmp-serde = "1.3.0"
|
||||
sea-orm = "0.12.15"
|
||||
|
@ -38,11 +40,12 @@ syn = "2.0.64"
|
|||
sysinfo = "0.30.12"
|
||||
thiserror = "1.0.61"
|
||||
tokio = "1.37.0"
|
||||
tokio-test = "0.4.4"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
url = "2.5.0"
|
||||
urlencoding = "2.1.3"
|
||||
web-push = { git = "https://github.com/pimeys/rust-web-push", rev = "40febe4085e3cef9cdfd539c315e3e945aba0656" }
|
||||
web-push = { git = "https://github.com/pimeys/rust-web-push.git", rev = "40febe4085e3cef9cdfd539c315e3e945aba0656" }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
|
|
@ -5,6 +5,10 @@ Critical security updates are indicated by the :warning: icon.
|
|||
- Server administrators should check [notice-for-admins.md](./notice-for-admins.md) as well.
|
||||
- Third-party client/bot developers may want to check [api-change.md](./api-change.md) as well.
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Fix bugs
|
||||
|
||||
## [v20240516](https://firefish.dev/firefish/firefish/-/merge_requests/10854/commits)
|
||||
|
||||
- Improve timeline UX (you can restore the original appearance by settings)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
BEGIN;
|
||||
|
||||
DELETE FROM "migrations" WHERE name IN (
|
||||
'AddBackTimezone1715351290096',
|
||||
'UserprofileJsonbToArray1714270605574',
|
||||
'DropUnusedUserprofileColumns1714259023878',
|
||||
'AntennaJsonbToArray1714192520471',
|
||||
|
@ -105,83 +106,6 @@ CREATE INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f" ON "note" USING "pgroonga" ("cw" p
|
|||
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;
|
||||
ALTER TABLE "access_token" ALTER "lastUsedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "ad" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "ad" ALTER "expiresAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "announcement" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "announcement" ALTER "updatedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "announcement_read" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "antenna" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "app" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "attestation_challenge" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "auth_session" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "blocking" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "channel" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "channel" ALTER "lastNotedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "channel_following" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "channel_note_pining" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "clip" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "drive_file" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "drive_folder" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "emoji" ALTER "updatedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "following" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "follow_request" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "gallery_like" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "gallery_post" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "gallery_post" ALTER "updatedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "instance" ALTER "caughtAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "instance" ALTER "infoUpdatedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "instance" ALTER "lastCommunicatedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "instance" ALTER "latestRequestReceivedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "instance" ALTER "latestRequestSentAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "messaging_message" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "moderation_log" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "muting" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "muting" ALTER "expiresAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "note" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "note" ALTER "updatedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "note_edit" ALTER "updatedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "note_favorite" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "note_reaction" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "note_thread_muting" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "note_watching" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "notification" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "page" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "page" ALTER "updatedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "page_like" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "password_reset_request" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "poll" ALTER "expiresAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "poll_vote" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "promo_note" ALTER "expiresAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "promo_read" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "registration_ticket" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "registry_item" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "registry_item" ALTER "updatedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "renote_muting" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "reply_muting" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "signin" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "sw_subscription" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "used_username" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user" ALTER "lastActiveDate" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user" ALTER "lastFetchedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user" ALTER "updatedAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user_group" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user_group_invitation" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user_group_invite" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user_group_joining" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user_ip" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user_list" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user_list_joining" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user_note_pining" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user_pending" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "user_security_key" ALTER "lastUsed" TYPE timestamp with time zone;
|
||||
ALTER TABLE "webhook" ALTER "createdAt" TYPE timestamp with time zone;
|
||||
ALTER TABLE "webhook" ALTER "latestSentAt" TYPE timestamp with time zone;
|
||||
|
||||
-- expand-note-edit
|
||||
ALTER TABLE "note_edit" DROP COLUMN "emojis";
|
||||
|
||||
|
|
|
@ -2,6 +2,28 @@
|
|||
|
||||
You can skip intermediate versions when upgrading from an old version, but please read the notices and follow the instructions for each intermediate version before [upgrading](./upgrade.md).
|
||||
|
||||
## Unreleased
|
||||
|
||||
### For all users
|
||||
|
||||
We regret to inform you that the upgrade may take a long time to fix a regression we have introduced. The time required to upgrade should be the same as [v20240413](<https://firefish.dev/firefish/firefish/-/blob/main/docs/notice-for-admins.md#v20240413>). This is not a security fix, so please upgrade your server when you have enough time. We are sorry for the inconvenience.
|
||||
|
||||
<details>
|
||||
|
||||
There are two data types in PostgreSQL to store time: `timestamptz` (`timestamp with time zone`) and `timestamp` (`timestamp without time zone`) [[ref]](<https://www.postgresql.org/docs/current/datatype-datetime.html>).
|
||||
|
||||
In Node.js, we manipulate the database using [TypeORM](<https://typeorm.io/>). TypeORM handles time data as a JavaScript `Date` object. Since `Date` doesn't have timezone information [[ref]](<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_components_and_time_zones>), we don't use the timezone information in the Node.js backend, and both `timestamptz` and `timestamp` behave in the same way. (Technically, the type names are a little confusing, and `timestamptz` (`timestamp with time zone`) doesn't store the timezone data. Please read PostgreSQL documentation for more information.)
|
||||
|
||||
In Rust, we manipulate the database using [SeaORM](<https://www.sea-ql.org/SeaORM/>), which does distinguish between `timestamptz` and `timestamp`. `timestamptz` is converted to [`DateTime<FixedOffset>`](<https://docs.rs/chrono/latest/chrono/struct.DateTime.html>) type, whereas `timestamp` is converted to [`NaiveDateTime`](<https://docs.rs/chrono/latest/chrono/struct.NaiveDateTime.html>).
|
||||
|
||||
We are using [napi-rs](<https://napi.rs/>) to implement some of the backend features in Rust, which did not support `DateTime<FixedOffset>`. We used to store time data as `timestamptz`, but we converted them to `timestamp` for this reason. As we don't use timezone data, we thought this was okay, and indeed it worked fine.
|
||||
|
||||
However, we did not consider the case of migrating a server (hardware) to another timezone. With `timestamp`, there may be inconsistencies in the time data if you migrate your server to another system with a different timezone setting (Docker/Podman users should not be affected by this, as UTC is always used in containers unless you explicitly set one).
|
||||
|
||||
Therefore, we have contributed to napi-rs to add support for `DateTime<FixedOffset>` (<https://github.com/napi-rs/napi-rs/pull/2074>) and decided to migrate back from `timestamp` to `timestamptz` to properly address this problem. The migration process takes time roughly proportional to the number of stored posts.
|
||||
|
||||
</details>
|
||||
|
||||
## v20240516
|
||||
|
||||
### For all users
|
||||
|
|
|
@ -74,6 +74,34 @@ mentions: "Mentions"
|
|||
directNotes: "Direct messages"
|
||||
cw: "Content warning"
|
||||
importAndExport: "Import/Export Data"
|
||||
importAndExportWarn: "The Import/Export Data feature is an experimental feature and
|
||||
implementation may change at any time without prior notice.\n
|
||||
Due to differences in the exported data of different software versions, the actual
|
||||
conditions of the import program, and the server health of the exported data link,
|
||||
the imported data may be incomplete or the access permissions may not be set
|
||||
correctly (for example, there is no access permission mark in the
|
||||
Mastodon/Akkoma/Pleroma exported data, so all posts makes public after import),
|
||||
so please be sure to check the imported data carefully integrity and configure
|
||||
the correct access permissions for it."
|
||||
importAndExportInfo: "Since some data cannot be obtained after the original account is
|
||||
frozen or the original server goes offline, it is strongly recommendedthat you import
|
||||
the data before the original account is frozen (migrated, logged out) or the original
|
||||
server goes offline.\n
|
||||
If the original account is frozen or the original server is offline but you have the
|
||||
original images, you can try uploading them to the network disk before importing the
|
||||
data, which may help with data import.\n
|
||||
Since some data is obtained from its server using your current account when importing
|
||||
data, data that the current account does not have permission to access will be regarded
|
||||
as broken. Please make adjustments including but not limited to access permissions,
|
||||
Manually following accounts and other methods allow the current account to obtain
|
||||
relevant data, so that the import program can normally obtain the data it needs to
|
||||
obtain to help you import.\n
|
||||
Since it is impossible to confirm whether the broken link content posted by someone other
|
||||
than you is posted by him/her, if there is broken link content posted by others in the
|
||||
discussion thread, the related content and subsequent replies will not be imported.\n
|
||||
Since data import is greatly affected by network communication, it is recommended that you
|
||||
pay attention to data recovery after a period of time. If the data is still not restored,
|
||||
you can try importing the same backup file again and try again."
|
||||
import: "Import"
|
||||
export: "Export"
|
||||
files: "Files"
|
||||
|
|
|
@ -61,6 +61,16 @@ mention: "提及"
|
|||
mentions: "提及"
|
||||
directNotes: "私信"
|
||||
importAndExport: "导入 / 导出数据"
|
||||
importAndExportWarn: "导入 / 导出数据功能是一项实验性功能,实现可能会随时变化而无预先通知。\n
|
||||
由于不同软件不同版本的导出数据、导入程序实际情况以及导出数据链接的服务器运行状况不同,导入的数据可能会不完整或未被正确设置访问权限
|
||||
(例如 Mastodon/Akkoma/Pleroma 导出数据内无访问权限标记,因此所有帖子导入后均为公开状态),因此请务必谨慎核对导入数据的完整性,
|
||||
并为其配置正确的访问权限。"
|
||||
importAndExportInfo: "由于原账号冻结或者原服务器下线后部分数据无法获取,因此强烈建议您在原账号冻结(迁移、注销)或原服务器下线前导入数据。\n
|
||||
在原账号冻结或者原服务器下线但您拥有原始图片的情况下,可以尝试在导入数据之前将其上传到网盘上,可能对数据导入有所帮助。\n
|
||||
由于导入数据时部分数据是使用您当前账号到其服务器上获取,因此当前账号无权访问的数据会视为断链。请通过包括但不限于访问权限调整、
|
||||
手动关注账户等方式让当前帐号可以获取到相关数据,以便导入程序能够正常获取到需要获取的数据从而帮助您进行导入。\n
|
||||
由于无法确认非您本人发表的断链内容的是否由其本人发表,因此如果讨论串内有其他人发表的断链内容,则相关内容以及后续回复不会被导入。\n
|
||||
由于数据导入受网络通信影响较大,因此建议您一段时间之后再关注数据恢复情况。如果数据仍未恢复可以尝试再次导入同样的备份文件重试一次。"
|
||||
import: "导入"
|
||||
export: "导出"
|
||||
files: "文件"
|
||||
|
|
|
@ -13,13 +13,15 @@ ci = []
|
|||
crate-type = ["cdylib", "lib"]
|
||||
|
||||
[dependencies]
|
||||
macro_rs = { workspace = true }
|
||||
macro-rs = { workspace = true }
|
||||
|
||||
napi = { workspace = true, optional = true, default-features = false, features = ["napi9", "tokio_rt", "chrono_date", "serde-json"] }
|
||||
napi-derive = { workspace = true, optional = true }
|
||||
|
||||
argon2 = { workspace = true, features = ["std"] }
|
||||
async-trait = { workspace = true }
|
||||
basen = { workspace = true }
|
||||
bb8 = { workspace = true }
|
||||
bcrypt = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
cuid2 = { workspace = true }
|
||||
|
@ -31,7 +33,7 @@ nom-exif = { workspace = true }
|
|||
once_cell = { workspace = true }
|
||||
openssl = { workspace = true, features = ["vendored"] }
|
||||
rand = { workspace = true }
|
||||
redis = { workspace = true }
|
||||
redis = { workspace = true, default-features = false, features = ["streams", "tokio-comp"] }
|
||||
regex = { workspace = true }
|
||||
rmp-serde = { workspace = true }
|
||||
sea-orm = { workspace = true, features = ["sqlx-postgres", "runtime-tokio-rustls"] }
|
||||
|
@ -50,6 +52,7 @@ web-push = { workspace = true }
|
|||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = { workspace = true }
|
||||
tokio-test = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = { workspace = true }
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
extern crate napi_build;
|
||||
|
||||
fn main() {
|
||||
// watch the version in the project root package.json
|
||||
println!("cargo:rerun-if-changed=../../package.json");
|
||||
|
||||
// napi
|
||||
napi_build::setup();
|
||||
}
|
||||
|
|
|
@ -191,18 +191,6 @@ export interface Config {
|
|||
authUrl: string
|
||||
driveUrl: string
|
||||
userAgent: string
|
||||
clientEntry: Manifest
|
||||
}
|
||||
export interface Manifest {
|
||||
file: string
|
||||
name: string
|
||||
src: string
|
||||
isEntry: boolean
|
||||
isDynamicEntry: boolean
|
||||
imports: Array<string>
|
||||
dynamicImports: Array<string>
|
||||
css: Array<string>
|
||||
assets: Array<string>
|
||||
}
|
||||
export function loadConfig(): Config
|
||||
export interface Acct {
|
||||
|
@ -211,6 +199,7 @@ export interface Acct {
|
|||
}
|
||||
export function stringToAcct(acct: string): Acct
|
||||
export function acctToString(acct: Acct): string
|
||||
export function greet(): void
|
||||
export function initializeRustLogger(): void
|
||||
export function showServerInfo(): void
|
||||
/**
|
||||
|
@ -323,7 +312,7 @@ export function memoryUsage(): Memory
|
|||
export function storageUsage(): Storage | null
|
||||
export interface AbuseUserReport {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
targetUserId: string
|
||||
reporterId: string
|
||||
assigneeId: string | null
|
||||
|
@ -335,12 +324,12 @@ export interface AbuseUserReport {
|
|||
}
|
||||
export interface AccessToken {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
token: string
|
||||
hash: string
|
||||
userId: string
|
||||
appId: string | null
|
||||
lastUsedAt: Date | null
|
||||
lastUsedAt: DateTimeWithTimeZone | null
|
||||
session: string | null
|
||||
name: string | null
|
||||
description: string | null
|
||||
|
@ -350,8 +339,8 @@ export interface AccessToken {
|
|||
}
|
||||
export interface Ad {
|
||||
id: string
|
||||
createdAt: Date
|
||||
expiresAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
expiresAt: DateTimeWithTimeZone
|
||||
place: string
|
||||
priority: string
|
||||
url: string
|
||||
|
@ -361,11 +350,11 @@ export interface Ad {
|
|||
}
|
||||
export interface Announcement {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
text: string
|
||||
title: string
|
||||
imageUrl: string | null
|
||||
updatedAt: Date | null
|
||||
updatedAt: DateTimeWithTimeZone | null
|
||||
showPopup: boolean
|
||||
isGoodNews: boolean
|
||||
}
|
||||
|
@ -373,11 +362,11 @@ export interface AnnouncementRead {
|
|||
id: string
|
||||
userId: string
|
||||
announcementId: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
}
|
||||
export interface Antenna {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
name: string
|
||||
src: AntennaSrcEnum
|
||||
|
@ -395,7 +384,7 @@ export interface Antenna {
|
|||
}
|
||||
export interface App {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string | null
|
||||
secret: string
|
||||
name: string
|
||||
|
@ -407,26 +396,26 @@ export interface AttestationChallenge {
|
|||
id: string
|
||||
userId: string
|
||||
challenge: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
registrationChallenge: boolean
|
||||
}
|
||||
export interface AuthSession {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
token: string
|
||||
userId: string | null
|
||||
appId: string
|
||||
}
|
||||
export interface Blocking {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
blockeeId: string
|
||||
blockerId: string
|
||||
}
|
||||
export interface Channel {
|
||||
id: string
|
||||
createdAt: Date
|
||||
lastNotedAt: Date | null
|
||||
createdAt: DateTimeWithTimeZone
|
||||
lastNotedAt: DateTimeWithTimeZone | null
|
||||
userId: string | null
|
||||
name: string
|
||||
description: string | null
|
||||
|
@ -436,19 +425,19 @@ export interface Channel {
|
|||
}
|
||||
export interface ChannelFollowing {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
followeeId: string
|
||||
followerId: string
|
||||
}
|
||||
export interface ChannelNotePining {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
channelId: string
|
||||
noteId: string
|
||||
}
|
||||
export interface Clip {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
name: string
|
||||
isPublic: boolean
|
||||
|
@ -461,7 +450,7 @@ export interface ClipNote {
|
|||
}
|
||||
export interface DriveFile {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string | null
|
||||
userHost: string | null
|
||||
md5: string
|
||||
|
@ -490,14 +479,14 @@ export interface DriveFile {
|
|||
}
|
||||
export interface DriveFolder {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
name: string
|
||||
userId: string | null
|
||||
parentId: string | null
|
||||
}
|
||||
export interface Emoji {
|
||||
id: string
|
||||
updatedAt: Date | null
|
||||
updatedAt: DateTimeWithTimeZone | null
|
||||
name: string
|
||||
host: string | null
|
||||
originalUrl: string
|
||||
|
@ -512,7 +501,7 @@ export interface Emoji {
|
|||
}
|
||||
export interface FollowRequest {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
followeeId: string
|
||||
followerId: string
|
||||
requestId: string | null
|
||||
|
@ -525,7 +514,7 @@ export interface FollowRequest {
|
|||
}
|
||||
export interface Following {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
followeeId: string
|
||||
followerId: string
|
||||
followerHost: string | null
|
||||
|
@ -537,14 +526,14 @@ export interface Following {
|
|||
}
|
||||
export interface GalleryLike {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
postId: string
|
||||
}
|
||||
export interface GalleryPost {
|
||||
id: string
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
updatedAt: DateTimeWithTimeZone
|
||||
title: string
|
||||
description: string | null
|
||||
userId: string
|
||||
|
@ -571,16 +560,16 @@ export interface Hashtag {
|
|||
}
|
||||
export interface Instance {
|
||||
id: string
|
||||
caughtAt: Date
|
||||
caughtAt: DateTimeWithTimeZone
|
||||
host: string
|
||||
usersCount: number
|
||||
notesCount: number
|
||||
followingCount: number
|
||||
followersCount: number
|
||||
latestRequestSentAt: Date | null
|
||||
latestRequestSentAt: DateTimeWithTimeZone | null
|
||||
latestStatus: number | null
|
||||
latestRequestReceivedAt: Date | null
|
||||
lastCommunicatedAt: Date
|
||||
latestRequestReceivedAt: DateTimeWithTimeZone | null
|
||||
lastCommunicatedAt: DateTimeWithTimeZone
|
||||
isNotResponding: boolean
|
||||
softwareName: string | null
|
||||
softwareVersion: string | null
|
||||
|
@ -589,7 +578,7 @@ export interface Instance {
|
|||
description: string | null
|
||||
maintainerName: string | null
|
||||
maintainerEmail: string | null
|
||||
infoUpdatedAt: Date | null
|
||||
infoUpdatedAt: DateTimeWithTimeZone | null
|
||||
isSuspended: boolean
|
||||
iconUrl: string | null
|
||||
themeColor: string | null
|
||||
|
@ -597,7 +586,7 @@ export interface Instance {
|
|||
}
|
||||
export interface MessagingMessage {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
recipientId: string | null
|
||||
text: string | null
|
||||
|
@ -701,7 +690,7 @@ export interface Migrations {
|
|||
}
|
||||
export interface ModerationLog {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
type: string
|
||||
info: Json
|
||||
|
@ -714,14 +703,14 @@ export interface MutedNote {
|
|||
}
|
||||
export interface Muting {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
muteeId: string
|
||||
muterId: string
|
||||
expiresAt: Date | null
|
||||
expiresAt: DateTimeWithTimeZone | null
|
||||
}
|
||||
export interface Note {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
replyId: string | null
|
||||
renoteId: string | null
|
||||
text: string | null
|
||||
|
@ -751,7 +740,7 @@ export interface Note {
|
|||
url: string | null
|
||||
channelId: string | null
|
||||
threadId: string | null
|
||||
updatedAt: Date | null
|
||||
updatedAt: DateTimeWithTimeZone | null
|
||||
lang: string | null
|
||||
}
|
||||
export interface NoteEdit {
|
||||
|
@ -760,12 +749,12 @@ export interface NoteEdit {
|
|||
text: string | null
|
||||
cw: string | null
|
||||
fileIds: Array<string>
|
||||
updatedAt: Date
|
||||
updatedAt: DateTimeWithTimeZone
|
||||
emojis: Array<string>
|
||||
}
|
||||
export interface NoteFavorite {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
noteId: string
|
||||
}
|
||||
|
@ -776,14 +765,14 @@ export interface NoteFile {
|
|||
}
|
||||
export interface NoteReaction {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
noteId: string
|
||||
reaction: string
|
||||
}
|
||||
export interface NoteThreadMuting {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
threadId: string
|
||||
}
|
||||
|
@ -798,14 +787,14 @@ export interface NoteUnread {
|
|||
}
|
||||
export interface NoteWatching {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
noteId: string
|
||||
noteUserId: string
|
||||
}
|
||||
export interface Notification {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
notifieeId: string
|
||||
notifierId: string | null
|
||||
isRead: boolean
|
||||
|
@ -822,8 +811,8 @@ export interface Notification {
|
|||
}
|
||||
export interface Page {
|
||||
id: string
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
updatedAt: DateTimeWithTimeZone
|
||||
title: string
|
||||
name: string
|
||||
summary: string | null
|
||||
|
@ -842,19 +831,19 @@ export interface Page {
|
|||
}
|
||||
export interface PageLike {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
pageId: string
|
||||
}
|
||||
export interface PasswordResetRequest {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
token: string
|
||||
userId: string
|
||||
}
|
||||
export interface Poll {
|
||||
noteId: string
|
||||
expiresAt: Date | null
|
||||
expiresAt: DateTimeWithTimeZone | null
|
||||
multiple: boolean
|
||||
choices: Array<string>
|
||||
votes: Array<number>
|
||||
|
@ -864,31 +853,31 @@ export interface Poll {
|
|||
}
|
||||
export interface PollVote {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
noteId: string
|
||||
choice: number
|
||||
}
|
||||
export interface PromoNote {
|
||||
noteId: string
|
||||
expiresAt: Date
|
||||
expiresAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
}
|
||||
export interface PromoRead {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
noteId: string
|
||||
}
|
||||
export interface RegistrationTicket {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
code: string
|
||||
}
|
||||
export interface RegistryItem {
|
||||
id: string
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
updatedAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
key: string
|
||||
scope: Array<string>
|
||||
|
@ -902,13 +891,13 @@ export interface Relay {
|
|||
}
|
||||
export interface RenoteMuting {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
muteeId: string
|
||||
muterId: string
|
||||
}
|
||||
export interface ReplyMuting {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
muteeId: string
|
||||
muterId: string
|
||||
}
|
||||
|
@ -994,7 +983,7 @@ export enum UserProfileMutingnotificationtypesEnum {
|
|||
}
|
||||
export interface Signin {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
ip: string
|
||||
headers: Json
|
||||
|
@ -1002,7 +991,7 @@ export interface Signin {
|
|||
}
|
||||
export interface SwSubscription {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
endpoint: string
|
||||
auth: string
|
||||
|
@ -1011,13 +1000,13 @@ export interface SwSubscription {
|
|||
}
|
||||
export interface UsedUsername {
|
||||
username: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
}
|
||||
export interface User {
|
||||
id: string
|
||||
createdAt: Date
|
||||
updatedAt: Date | null
|
||||
lastFetchedAt: Date | null
|
||||
createdAt: DateTimeWithTimeZone
|
||||
updatedAt: DateTimeWithTimeZone | null
|
||||
lastFetchedAt: DateTimeWithTimeZone | null
|
||||
username: string
|
||||
usernameLower: string
|
||||
name: string | null
|
||||
|
@ -1043,7 +1032,7 @@ export interface User {
|
|||
token: string | null
|
||||
isExplorable: boolean
|
||||
followersUri: string | null
|
||||
lastActiveDate: Date | null
|
||||
lastActiveDate: DateTimeWithTimeZone | null
|
||||
hideOnlineStatus: boolean
|
||||
isDeleted: boolean
|
||||
driveCapacityOverrideMb: number | null
|
||||
|
@ -1055,32 +1044,32 @@ export interface User {
|
|||
}
|
||||
export interface UserGroup {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
name: string
|
||||
userId: string
|
||||
isPrivate: boolean
|
||||
}
|
||||
export interface UserGroupInvitation {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
userGroupId: string
|
||||
}
|
||||
export interface UserGroupInvite {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
userGroupId: string
|
||||
}
|
||||
export interface UserGroupJoining {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
userGroupId: string
|
||||
}
|
||||
export interface UserIp {
|
||||
id: number
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
ip: string
|
||||
}
|
||||
|
@ -1091,25 +1080,25 @@ export interface UserKeypair {
|
|||
}
|
||||
export interface UserList {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
name: string
|
||||
}
|
||||
export interface UserListJoining {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
userListId: string
|
||||
}
|
||||
export interface UserNotePining {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
noteId: string
|
||||
}
|
||||
export interface UserPending {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
code: string
|
||||
username: string
|
||||
email: string
|
||||
|
@ -1161,19 +1150,19 @@ export interface UserSecurityKey {
|
|||
id: string
|
||||
userId: string
|
||||
publicKey: string
|
||||
lastUsed: Date
|
||||
lastUsed: DateTimeWithTimeZone
|
||||
name: string
|
||||
}
|
||||
export interface Webhook {
|
||||
id: string
|
||||
createdAt: Date
|
||||
createdAt: DateTimeWithTimeZone
|
||||
userId: string
|
||||
name: string
|
||||
on: Array<string>
|
||||
url: string
|
||||
secret: string
|
||||
active: boolean
|
||||
latestSentAt: Date | null
|
||||
latestSentAt: DateTimeWithTimeZone | null
|
||||
latestStatus: number | null
|
||||
}
|
||||
export function updateAntennasOnNewNote(note: Note, noteAuthor: Acct, noteMutedUsers: Array<string>): Promise<void>
|
||||
|
@ -1285,22 +1274,23 @@ export enum PushNotificationKind {
|
|||
ReadAllChats = 'readAllChats',
|
||||
ReadAllChatsInTheRoom = 'readAllChatsInTheRoom',
|
||||
ReadNotifications = 'readNotifications',
|
||||
ReadAllNotifications = 'readAllNotifications'
|
||||
ReadAllNotifications = 'readAllNotifications',
|
||||
Mastodon = 'mastodon'
|
||||
}
|
||||
export function sendPushNotification(receiverUserId: string, kind: PushNotificationKind, content: any): Promise<void>
|
||||
export function publishToChannelStream(channelId: string, userId: string): void
|
||||
export function publishToChannelStream(channelId: string, userId: string): Promise<void>
|
||||
export enum ChatEvent {
|
||||
Message = 'message',
|
||||
Read = 'read',
|
||||
Deleted = 'deleted',
|
||||
Typing = 'typing'
|
||||
}
|
||||
export function publishToChatStream(senderUserId: string, receiverUserId: string, kind: ChatEvent, object: any): void
|
||||
export function publishToChatStream(senderUserId: string, receiverUserId: string, kind: ChatEvent, object: any): Promise<void>
|
||||
export enum ChatIndexEvent {
|
||||
Message = 'message',
|
||||
Read = 'read'
|
||||
}
|
||||
export function publishToChatIndexStream(userId: string, kind: ChatIndexEvent, object: any): void
|
||||
export function publishToChatIndexStream(userId: string, kind: ChatIndexEvent, object: any): Promise<void>
|
||||
export interface PackedEmoji {
|
||||
id: string
|
||||
aliases: Array<string>
|
||||
|
@ -1312,15 +1302,15 @@ export interface PackedEmoji {
|
|||
width: number | null
|
||||
height: number | null
|
||||
}
|
||||
export function publishToBroadcastStream(emoji: PackedEmoji): void
|
||||
export function publishToGroupChatStream(groupId: string, kind: ChatEvent, object: any): void
|
||||
export function publishToBroadcastStream(emoji: PackedEmoji): Promise<void>
|
||||
export function publishToGroupChatStream(groupId: string, kind: ChatEvent, object: any): Promise<void>
|
||||
export interface AbuseUserReportLike {
|
||||
id: string
|
||||
targetUserId: string
|
||||
reporterId: string
|
||||
comment: string
|
||||
}
|
||||
export function publishToModerationStream(moderatorId: string, report: AbuseUserReportLike): void
|
||||
export function publishToModerationStream(moderatorId: string, report: AbuseUserReportLike): Promise<void>
|
||||
export function getTimestamp(id: string): number
|
||||
/**
|
||||
* The generated ID results in the form of `[8 chars timestamp] + [cuid2]`.
|
||||
|
|
|
@ -310,7 +310,7 @@ if (!nativeBinding) {
|
|||
throw new Error(`Failed to load native binding`)
|
||||
}
|
||||
|
||||
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, initializeRustLogger, showServerInfo, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isQuote, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, updateAntennasOnNewNote, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, Protocol, Inbound, Outbound, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
|
||||
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, greet, initializeRustLogger, showServerInfo, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isQuote, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, updateAntennasOnNewNote, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, Protocol, Inbound, Outbound, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
|
||||
|
||||
module.exports.SECOND = SECOND
|
||||
module.exports.MINUTE = MINUTE
|
||||
|
@ -323,6 +323,7 @@ module.exports.loadEnv = loadEnv
|
|||
module.exports.loadConfig = loadConfig
|
||||
module.exports.stringToAcct = stringToAcct
|
||||
module.exports.acctToString = acctToString
|
||||
module.exports.greet = greet
|
||||
module.exports.initializeRustLogger = initializeRustLogger
|
||||
module.exports.showServerInfo = showServerInfo
|
||||
module.exports.isBlockedServer = isBlockedServer
|
||||
|
|
|
@ -3,6 +3,8 @@ use serde::Deserialize;
|
|||
use std::env;
|
||||
use std::fs;
|
||||
|
||||
pub const VERSION: &str = macro_rs::read_version_from_package_json!();
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
|
@ -231,34 +233,6 @@ pub struct Config {
|
|||
pub auth_url: String,
|
||||
pub drive_url: String,
|
||||
pub user_agent: String,
|
||||
pub client_entry: Manifest,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct Meta {
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
struct ManifestJson {
|
||||
#[serde(rename = "src/init.ts")]
|
||||
pub init_ts: Manifest,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object, use_nullable = false)]
|
||||
pub struct Manifest {
|
||||
pub file: String,
|
||||
pub name: String,
|
||||
pub src: String,
|
||||
pub is_entry: bool,
|
||||
pub is_dynamic_entry: bool,
|
||||
pub imports: Vec<String>,
|
||||
pub dynamic_imports: Vec<String>,
|
||||
pub css: Vec<String>,
|
||||
pub assets: Vec<String>,
|
||||
}
|
||||
|
||||
fn read_config_file() -> ServerConfig {
|
||||
|
@ -280,28 +254,10 @@ fn read_config_file() -> ServerConfig {
|
|||
data
|
||||
}
|
||||
|
||||
fn read_meta() -> Meta {
|
||||
let cwd = env::current_dir().unwrap();
|
||||
let meta_json = fs::File::open(cwd.join("../../built/meta.json"))
|
||||
.expect("Failed to open 'built/meta.json'");
|
||||
serde_json::from_reader(meta_json).expect("Failed to parse built/meta.json")
|
||||
}
|
||||
|
||||
fn read_manifest() -> Manifest {
|
||||
let cwd = env::current_dir().unwrap();
|
||||
let manifest_json = fs::File::open(cwd.join("../../built/_client_dist_/manifest.json"))
|
||||
.expect("Failed to open 'built/_client_dist_/manifest.json'");
|
||||
let manifest: ManifestJson = serde_json::from_reader(manifest_json)
|
||||
.expect("Failed to parse built/_client_dist_/manifest.json");
|
||||
|
||||
manifest.init_ts
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn load_config() -> Config {
|
||||
let server_config = read_config_file();
|
||||
let version = read_meta().version;
|
||||
let manifest = read_manifest();
|
||||
let version = VERSION.to_owned();
|
||||
let url = url::Url::parse(&server_config.url).expect("Config url is invalid");
|
||||
let hostname = url
|
||||
.host_str()
|
||||
|
@ -379,7 +335,6 @@ pub fn load_config() -> Config {
|
|||
redis_key_prefix,
|
||||
scheme,
|
||||
ws_scheme,
|
||||
client_entry: manifest,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::database::{redis_conn, redis_key};
|
||||
use redis::{Commands, RedisError};
|
||||
use crate::database::{redis_conn, redis_key, RedisConnError};
|
||||
use redis::{AsyncCommands, RedisError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(strum::Display, Debug)]
|
||||
|
@ -18,11 +18,13 @@ pub enum Category {
|
|||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Redis error: {0}")]
|
||||
RedisError(#[from] RedisError),
|
||||
RedisErr(#[from] RedisError),
|
||||
#[error("Redis connection error: {0}")]
|
||||
RedisConnErr(#[from] RedisConnError),
|
||||
#[error("Data serialization error: {0}")]
|
||||
SerializeError(#[from] rmp_serde::encode::Error),
|
||||
SerializeErr(#[from] rmp_serde::encode::Error),
|
||||
#[error("Data deserialization error: {0}")]
|
||||
DeserializeError(#[from] rmp_serde::decode::Error),
|
||||
DeserializeErr(#[from] rmp_serde::decode::Error),
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -54,26 +56,31 @@ fn wildcard(category: Category) -> String {
|
|||
///
|
||||
/// ```
|
||||
/// # use backend_rs::database::cache;
|
||||
/// # tokio_test::block_on(async {
|
||||
/// let key = "apple";
|
||||
/// let data = "I want to cache this string".to_string();
|
||||
///
|
||||
/// // caches the data for 10 seconds
|
||||
/// cache::set(key, &data, 10);
|
||||
/// cache::set(key, &data, 10).await;
|
||||
///
|
||||
/// // get the cache
|
||||
/// let cached_data = cache::get::<String>(key).unwrap();
|
||||
/// let cached_data = cache::get::<String>(key).await.unwrap();
|
||||
/// assert_eq!(data, cached_data.unwrap());
|
||||
/// # })
|
||||
/// ```
|
||||
pub fn set<V: for<'a> Deserialize<'a> + Serialize>(
|
||||
pub async fn set<V: for<'a> Deserialize<'a> + Serialize>(
|
||||
key: &str,
|
||||
value: &V,
|
||||
expire_seconds: u64,
|
||||
) -> Result<(), Error> {
|
||||
redis_conn()?.set_ex(
|
||||
prefix_key(key),
|
||||
rmp_serde::encode::to_vec(&value)?,
|
||||
expire_seconds,
|
||||
)?;
|
||||
redis_conn()
|
||||
.await?
|
||||
.set_ex(
|
||||
prefix_key(key),
|
||||
rmp_serde::encode::to_vec(&value)?,
|
||||
expire_seconds,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -90,22 +97,24 @@ pub fn set<V: for<'a> Deserialize<'a> + Serialize>(
|
|||
///
|
||||
/// ```
|
||||
/// # use backend_rs::database::cache;
|
||||
/// # tokio_test::block_on(async {
|
||||
/// let key = "banana";
|
||||
/// let data = "I want to cache this string".to_string();
|
||||
///
|
||||
/// // set cache
|
||||
/// cache::set(key, &data, 10).unwrap();
|
||||
/// cache::set(key, &data, 10).await.unwrap();
|
||||
///
|
||||
/// // get cache
|
||||
/// let cached_data = cache::get::<String>(key).unwrap();
|
||||
/// let cached_data = cache::get::<String>(key).await.unwrap();
|
||||
/// assert_eq!(data, cached_data.unwrap());
|
||||
///
|
||||
/// // get nonexistent (or expired) cache
|
||||
/// let no_cache = cache::get::<String>("nonexistent").unwrap();
|
||||
/// let no_cache = cache::get::<String>("nonexistent").await.unwrap();
|
||||
/// assert!(no_cache.is_none());
|
||||
/// # })
|
||||
/// ```
|
||||
pub fn get<V: for<'a> Deserialize<'a> + Serialize>(key: &str) -> Result<Option<V>, Error> {
|
||||
let serialized_value: Option<Vec<u8>> = redis_conn()?.get(prefix_key(key))?;
|
||||
pub async fn get<V: for<'a> Deserialize<'a> + Serialize>(key: &str) -> Result<Option<V>, Error> {
|
||||
let serialized_value: Option<Vec<u8>> = redis_conn().await?.get(prefix_key(key)).await?;
|
||||
Ok(match serialized_value {
|
||||
Some(v) => Some(rmp_serde::from_slice::<V>(v.as_ref())?),
|
||||
None => None,
|
||||
|
@ -125,22 +134,24 @@ pub fn get<V: for<'a> Deserialize<'a> + Serialize>(key: &str) -> Result<Option<V
|
|||
///
|
||||
/// ```
|
||||
/// # use backend_rs::database::cache;
|
||||
/// # tokio_test::block_on(async {
|
||||
/// let key = "chocolate";
|
||||
/// let value = "I want to cache this string".to_string();
|
||||
///
|
||||
/// // set cache
|
||||
/// cache::set(key, &value, 10).unwrap();
|
||||
/// cache::set(key, &value, 10).await.unwrap();
|
||||
///
|
||||
/// // delete the cache
|
||||
/// cache::delete("foo").unwrap();
|
||||
/// cache::delete("nonexistent").unwrap(); // this is okay
|
||||
/// cache::delete("foo").await.unwrap();
|
||||
/// cache::delete("nonexistent").await.unwrap(); // this is okay
|
||||
///
|
||||
/// // the cache is gone
|
||||
/// let cached_value = cache::get::<String>("foo").unwrap();
|
||||
/// let cached_value = cache::get::<String>("foo").await.unwrap();
|
||||
/// assert!(cached_value.is_none());
|
||||
/// # })
|
||||
/// ```
|
||||
pub fn delete(key: &str) -> Result<(), Error> {
|
||||
Ok(redis_conn()?.del(prefix_key(key))?)
|
||||
pub async fn delete(key: &str) -> Result<(), Error> {
|
||||
Ok(redis_conn().await?.del(prefix_key(key)).await?)
|
||||
}
|
||||
|
||||
/// Sets a Redis cache under a `category`.
|
||||
|
@ -154,13 +165,13 @@ pub fn delete(key: &str) -> Result<(), Error> {
|
|||
/// * `key` - key (will be prefixed automatically)
|
||||
/// * `value` - (de)serializable value
|
||||
/// * `expire_seconds` - TTL
|
||||
pub fn set_one<V: for<'a> Deserialize<'a> + Serialize>(
|
||||
pub async fn set_one<V: for<'a> Deserialize<'a> + Serialize>(
|
||||
category: Category,
|
||||
key: &str,
|
||||
value: &V,
|
||||
expire_seconds: u64,
|
||||
) -> Result<(), Error> {
|
||||
set(&categorize(category, key), value, expire_seconds)
|
||||
set(&categorize(category, key), value, expire_seconds).await
|
||||
}
|
||||
|
||||
/// Gets a Redis cache under a `category`.
|
||||
|
@ -171,11 +182,11 @@ pub fn set_one<V: for<'a> Deserialize<'a> + Serialize>(
|
|||
///
|
||||
/// * `category` - one of [Category]
|
||||
/// * `key` - key (will be prefixed automatically)
|
||||
pub fn get_one<V: for<'a> Deserialize<'a> + Serialize>(
|
||||
pub async fn get_one<V: for<'a> Deserialize<'a> + Serialize>(
|
||||
category: Category,
|
||||
key: &str,
|
||||
) -> Result<Option<V>, Error> {
|
||||
get(&categorize(category, key))
|
||||
get(&categorize(category, key)).await
|
||||
}
|
||||
|
||||
/// Deletes a Redis cache under a `category`.
|
||||
|
@ -186,8 +197,8 @@ pub fn get_one<V: for<'a> Deserialize<'a> + Serialize>(
|
|||
///
|
||||
/// * `category` - one of [Category]
|
||||
/// * `key` - key (will be prefixed automatically)
|
||||
pub fn delete_one(category: Category, key: &str) -> Result<(), Error> {
|
||||
delete(&categorize(category, key))
|
||||
pub async fn delete_one(category: Category, key: &str) -> Result<(), Error> {
|
||||
delete(&categorize(category, key)).await
|
||||
}
|
||||
|
||||
/// Deletes all Redis caches under a `category`.
|
||||
|
@ -195,28 +206,27 @@ pub fn delete_one(category: Category, key: &str) -> Result<(), Error> {
|
|||
/// ## Arguments
|
||||
///
|
||||
/// * `category` - one of [Category]
|
||||
pub fn delete_all(category: Category) -> Result<(), Error> {
|
||||
let mut redis = redis_conn()?;
|
||||
let keys: Vec<Vec<u8>> = redis.keys(wildcard(category))?;
|
||||
pub async fn delete_all(category: Category) -> Result<(), Error> {
|
||||
let mut redis = redis_conn().await?;
|
||||
let keys: Vec<Vec<u8>> = redis.keys(wildcard(category)).await?;
|
||||
|
||||
if !keys.is_empty() {
|
||||
redis.del(keys)?
|
||||
redis.del(keys).await?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: set_all(), get_all()
|
||||
// TODO: get_all()
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use crate::database::cache::delete_one;
|
||||
|
||||
use super::{delete_all, get, get_one, set, set_one, Category::Test};
|
||||
use crate::database::cache::delete_one;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn set_get_expire() {
|
||||
#[tokio::test]
|
||||
async fn set_get_expire() {
|
||||
#[derive(serde::Deserialize, serde::Serialize, PartialEq, Debug)]
|
||||
struct Data {
|
||||
id: u32,
|
||||
|
@ -235,13 +245,13 @@ mod unit_test {
|
|||
kind: "prime number".to_string(),
|
||||
};
|
||||
|
||||
set(key_1, &value_1, 1).unwrap();
|
||||
set(key_2, &value_2, 1).unwrap();
|
||||
set(key_3, &value_3, 1).unwrap();
|
||||
set(key_1, &value_1, 1).await.unwrap();
|
||||
set(key_2, &value_2, 1).await.unwrap();
|
||||
set(key_3, &value_3, 1).await.unwrap();
|
||||
|
||||
let cached_value_1: Vec<i32> = get(key_1).unwrap().unwrap();
|
||||
let cached_value_2: String = get(key_2).unwrap().unwrap();
|
||||
let cached_value_3: Data = get(key_3).unwrap().unwrap();
|
||||
let cached_value_1: Vec<i32> = get(key_1).await.unwrap().unwrap();
|
||||
let cached_value_2: String = get(key_2).await.unwrap().unwrap();
|
||||
let cached_value_3: Data = get(key_3).await.unwrap().unwrap();
|
||||
|
||||
assert_eq!(value_1, cached_value_1);
|
||||
assert_eq!(value_2, cached_value_2);
|
||||
|
@ -250,17 +260,17 @@ mod unit_test {
|
|||
// wait for the cache to expire
|
||||
std::thread::sleep(std::time::Duration::from_millis(1100));
|
||||
|
||||
let expired_value_1: Option<Vec<i32>> = get(key_1).unwrap();
|
||||
let expired_value_2: Option<Vec<i32>> = get(key_2).unwrap();
|
||||
let expired_value_3: Option<Vec<i32>> = get(key_3).unwrap();
|
||||
let expired_value_1: Option<Vec<i32>> = get(key_1).await.unwrap();
|
||||
let expired_value_2: Option<Vec<i32>> = get(key_2).await.unwrap();
|
||||
let expired_value_3: Option<Vec<i32>> = get(key_3).await.unwrap();
|
||||
|
||||
assert!(expired_value_1.is_none());
|
||||
assert!(expired_value_2.is_none());
|
||||
assert!(expired_value_3.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_category() {
|
||||
#[tokio::test]
|
||||
async fn use_category() {
|
||||
let key_1 = "fire";
|
||||
let key_2 = "fish";
|
||||
let key_3 = "awawa";
|
||||
|
@ -269,24 +279,30 @@ mod unit_test {
|
|||
let value_2 = 998244353u32;
|
||||
let value_3 = 'あ';
|
||||
|
||||
set_one(Test, key_1, &value_1, 5 * 60).unwrap();
|
||||
set_one(Test, key_2, &value_2, 5 * 60).unwrap();
|
||||
set_one(Test, key_3, &value_3, 5 * 60).unwrap();
|
||||
set_one(Test, key_1, &value_1, 5 * 60).await.unwrap();
|
||||
set_one(Test, key_2, &value_2, 5 * 60).await.unwrap();
|
||||
set_one(Test, key_3, &value_3, 5 * 60).await.unwrap();
|
||||
|
||||
assert_eq!(get_one::<String>(Test, key_1).unwrap().unwrap(), value_1);
|
||||
assert_eq!(get_one::<u32>(Test, key_2).unwrap().unwrap(), value_2);
|
||||
assert_eq!(get_one::<char>(Test, key_3).unwrap().unwrap(), value_3);
|
||||
assert_eq!(
|
||||
get_one::<String>(Test, key_1).await.unwrap().unwrap(),
|
||||
value_1
|
||||
);
|
||||
assert_eq!(get_one::<u32>(Test, key_2).await.unwrap().unwrap(), value_2);
|
||||
assert_eq!(
|
||||
get_one::<char>(Test, key_3).await.unwrap().unwrap(),
|
||||
value_3
|
||||
);
|
||||
|
||||
delete_one(Test, key_1).unwrap();
|
||||
delete_one(Test, key_1).await.unwrap();
|
||||
|
||||
assert!(get_one::<String>(Test, key_1).unwrap().is_none());
|
||||
assert!(get_one::<u32>(Test, key_2).unwrap().is_some());
|
||||
assert!(get_one::<char>(Test, key_3).unwrap().is_some());
|
||||
assert!(get_one::<String>(Test, key_1).await.unwrap().is_none());
|
||||
assert!(get_one::<u32>(Test, key_2).await.unwrap().is_some());
|
||||
assert!(get_one::<char>(Test, key_3).await.unwrap().is_some());
|
||||
|
||||
delete_all(Test).unwrap();
|
||||
delete_all(Test).await.unwrap();
|
||||
|
||||
assert!(get_one::<String>(Test, key_1).unwrap().is_none());
|
||||
assert!(get_one::<u32>(Test, key_2).unwrap().is_none());
|
||||
assert!(get_one::<char>(Test, key_3).unwrap().is_none());
|
||||
assert!(get_one::<String>(Test, key_1).await.unwrap().is_none());
|
||||
assert!(get_one::<u32>(Test, key_2).await.unwrap().is_none());
|
||||
assert!(get_one::<char>(Test, key_3).await.unwrap().is_none());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
pub use postgresql::db_conn;
|
||||
pub use redis::key as redis_key;
|
||||
pub use redis::redis_conn;
|
||||
pub use redis::RedisConnError;
|
||||
|
||||
pub mod cache;
|
||||
pub mod postgresql;
|
||||
|
|
|
@ -1,10 +1,50 @@
|
|||
use crate::config::CONFIG;
|
||||
use once_cell::sync::OnceCell;
|
||||
use redis::{Client, Connection, RedisError};
|
||||
use async_trait::async_trait;
|
||||
use bb8::{ManageConnection, Pool, PooledConnection, RunError};
|
||||
use redis::{aio::MultiplexedConnection, Client, ErrorKind, IntoConnectionInfo, RedisError};
|
||||
use tokio::sync::OnceCell;
|
||||
|
||||
static REDIS_CLIENT: OnceCell<Client> = OnceCell::new();
|
||||
/// A `bb8::ManageConnection` for `redis::Client::get_multiplexed_async_connection`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RedisConnectionManager {
|
||||
client: Client,
|
||||
}
|
||||
|
||||
fn init_redis() -> Result<Client, RedisError> {
|
||||
impl RedisConnectionManager {
|
||||
/// Create a new `RedisConnectionManager`.
|
||||
/// See `redis::Client::open` for a description of the parameter types.
|
||||
pub fn new<T: IntoConnectionInfo>(info: T) -> Result<Self, RedisError> {
|
||||
Ok(Self {
|
||||
client: Client::open(info.into_connection_info()?)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ManageConnection for RedisConnectionManager {
|
||||
type Connection = MultiplexedConnection;
|
||||
type Error = RedisError;
|
||||
|
||||
async fn connect(&self) -> Result<Self::Connection, Self::Error> {
|
||||
self.client.get_multiplexed_async_connection().await
|
||||
}
|
||||
|
||||
async fn is_valid(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> {
|
||||
let pong: String = redis::cmd("PING").query_async(conn).await?;
|
||||
match pong.as_str() {
|
||||
"PONG" => Ok(()),
|
||||
_ => Err((ErrorKind::ResponseError, "ping request").into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn has_broken(&self, _: &mut Self::Connection) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
static CONN_POOL: OnceCell<Pool<RedisConnectionManager>> = OnceCell::const_new();
|
||||
|
||||
async fn init_conn_pool() -> Result<(), RedisError> {
|
||||
let redis_url = {
|
||||
let mut params = vec!["redis://".to_owned()];
|
||||
|
||||
|
@ -27,16 +67,40 @@ fn init_redis() -> Result<Client, RedisError> {
|
|||
params.concat()
|
||||
};
|
||||
|
||||
tracing::info!("Initializing Redis client");
|
||||
tracing::info!("Initializing connection manager");
|
||||
let manager = RedisConnectionManager::new(redis_url)?;
|
||||
|
||||
Client::open(redis_url)
|
||||
tracing::info!("Creating connection pool");
|
||||
let pool = Pool::builder().build(manager).await?;
|
||||
|
||||
CONN_POOL.get_or_init(|| async { pool }).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn redis_conn() -> Result<Connection, RedisError> {
|
||||
match REDIS_CLIENT.get() {
|
||||
Some(client) => Ok(client.get_connection()?),
|
||||
None => init_redis()?.get_connection(),
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum RedisConnError {
|
||||
#[error("Failed to initialize Redis connection pool: {0}")]
|
||||
RedisErr(RedisError),
|
||||
#[error("Redis connection pool error: {0}")]
|
||||
Bb8PoolErr(RunError<RedisError>),
|
||||
}
|
||||
|
||||
pub async fn redis_conn(
|
||||
) -> Result<PooledConnection<'static, RedisConnectionManager>, RedisConnError> {
|
||||
if !CONN_POOL.initialized() {
|
||||
let init_res = init_conn_pool().await;
|
||||
|
||||
if let Err(err) = init_res {
|
||||
return Err(RedisConnError::RedisErr(err));
|
||||
}
|
||||
}
|
||||
|
||||
CONN_POOL
|
||||
.get()
|
||||
.unwrap()
|
||||
.get()
|
||||
.await
|
||||
.map_err(RedisConnError::Bb8PoolErr)
|
||||
}
|
||||
|
||||
/// prefix redis key
|
||||
|
@ -49,23 +113,26 @@ pub fn key(key: impl ToString) -> String {
|
|||
mod unit_test {
|
||||
use super::redis_conn;
|
||||
use pretty_assertions::assert_eq;
|
||||
use redis::Commands;
|
||||
use redis::AsyncCommands;
|
||||
|
||||
#[test]
|
||||
fn connect() {
|
||||
assert!(redis_conn().is_ok());
|
||||
assert!(redis_conn().is_ok());
|
||||
#[tokio::test]
|
||||
async fn connect() {
|
||||
assert!(redis_conn().await.is_ok());
|
||||
assert!(redis_conn().await.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn access() {
|
||||
let mut redis = redis_conn().unwrap();
|
||||
#[tokio::test]
|
||||
async fn access() {
|
||||
let mut redis = redis_conn().await.unwrap();
|
||||
|
||||
let key = "CARGO_UNIT_TEST_KEY";
|
||||
let value = "CARGO_UNIT_TEST_VALUE";
|
||||
|
||||
assert_eq!(redis.set::<&str, &str, String>(key, value).unwrap(), "OK");
|
||||
assert_eq!(redis.get::<&str, String>(key).unwrap(), value);
|
||||
assert_eq!(redis.del::<&str, u32>(key).unwrap(), 1);
|
||||
assert_eq!(
|
||||
redis.set::<&str, &str, String>(key, value).await.unwrap(),
|
||||
"OK"
|
||||
);
|
||||
assert_eq!(redis.get::<&str, String>(key).await.unwrap(), value);
|
||||
assert_eq!(redis.del::<&str, u32>(key).await.unwrap(), 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
use crate::config::server::VERSION;
|
||||
|
||||
const GREETING_MESSAGE: &str = "\
|
||||
███████╗██╗██████╗ ███████╗███████╗██╗███████╗██╗ ██╗ ○ ▄ ▄
|
||||
██╔════╝██║██╔══██╗██╔════╝██╔════╝██║██╔════╝██║ ██║ ⚬ █▄▄ █▄▄
|
||||
█████╗ ██║██████╔╝█████╗ █████╗ ██║███████╗███████║ ▄▄▄▄▄▄ ▄
|
||||
██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║╚════██║██╔══██║ █ █ █▄▄
|
||||
██║ ██║██║ ██║███████╗██║ ██║███████║██║ ██║ █ ● ● █
|
||||
╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ▀▄▄▄▄▄▄▀
|
||||
Firefish is an open-source decentralized microblogging platform.
|
||||
If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish
|
||||
";
|
||||
|
||||
#[crate::export]
|
||||
pub fn greet() {
|
||||
println!("{}", GREETING_MESSAGE);
|
||||
|
||||
tracing::info!("Welcome to Firefish!");
|
||||
tracing::info!("Firefish {VERSION}");
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
pub mod greet;
|
||||
pub mod log;
|
||||
pub mod system_info;
|
||||
|
|
|
@ -104,7 +104,8 @@ pub async fn check_hit_antenna(
|
|||
|
||||
let db = db_conn().await?;
|
||||
|
||||
let blocked_user_ids: Vec<String> = cache::get_one(cache::Category::Block, ¬e.user_id)?
|
||||
let blocked_user_ids: Vec<String> = cache::get_one(cache::Category::Block, ¬e.user_id)
|
||||
.await?
|
||||
.unwrap_or({
|
||||
// cache miss
|
||||
let blocks = blocking::Entity::find()
|
||||
|
@ -114,7 +115,7 @@ pub async fn check_hit_antenna(
|
|||
.into_tuple::<String>()
|
||||
.all(db)
|
||||
.await?;
|
||||
cache::set_one(cache::Category::Block, ¬e.user_id, &blocks, 10 * 60)?;
|
||||
cache::set_one(cache::Category::Block, ¬e.user_id, &blocks, 10 * 60).await?;
|
||||
blocks
|
||||
});
|
||||
|
||||
|
@ -125,23 +126,26 @@ pub async fn check_hit_antenna(
|
|||
|
||||
if [NoteVisibilityEnum::Home, NoteVisibilityEnum::Followers].contains(¬e.visibility) {
|
||||
let following_user_ids: Vec<String> =
|
||||
cache::get_one(cache::Category::Follow, &antenna.user_id)?.unwrap_or({
|
||||
// cache miss
|
||||
let following = following::Entity::find()
|
||||
.select_only()
|
||||
.column(following::Column::FolloweeId)
|
||||
.filter(following::Column::FollowerId.eq(&antenna.user_id))
|
||||
.into_tuple::<String>()
|
||||
.all(db)
|
||||
cache::get_one(cache::Category::Follow, &antenna.user_id)
|
||||
.await?
|
||||
.unwrap_or({
|
||||
// cache miss
|
||||
let following = following::Entity::find()
|
||||
.select_only()
|
||||
.column(following::Column::FolloweeId)
|
||||
.filter(following::Column::FollowerId.eq(&antenna.user_id))
|
||||
.into_tuple::<String>()
|
||||
.all(db)
|
||||
.await?;
|
||||
cache::set_one(
|
||||
cache::Category::Follow,
|
||||
&antenna.user_id,
|
||||
&following,
|
||||
10 * 60,
|
||||
)
|
||||
.await?;
|
||||
cache::set_one(
|
||||
cache::Category::Follow,
|
||||
&antenna.user_id,
|
||||
&following,
|
||||
10 * 60,
|
||||
)?;
|
||||
following
|
||||
});
|
||||
following
|
||||
});
|
||||
|
||||
// if the antenna owner is not following the note author, return false
|
||||
if !following_user_ids.contains(¬e.user_id) {
|
||||
|
|
|
@ -61,122 +61,106 @@ mod unit_test {
|
|||
let katakana_2 = r#"/[ア-ン]/u"#.to_string();
|
||||
let emoji = r#"/[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}]/u"#.to_string();
|
||||
|
||||
assert!(check_word_mute_impl(
|
||||
&texts,
|
||||
&vec![],
|
||||
&vec!["/the/i".to_string()]
|
||||
));
|
||||
assert!(check_word_mute_impl(&texts, &[], &["/the/i".to_string()]));
|
||||
|
||||
assert!(!check_word_mute_impl(
|
||||
&texts,
|
||||
&vec![],
|
||||
&vec!["/the/".to_string()]
|
||||
));
|
||||
assert!(!check_word_mute_impl(&texts, &[], &["/the/".to_string()]));
|
||||
|
||||
assert!(check_word_mute_impl(&texts, &[], &["/QuICk/i".to_string()]));
|
||||
|
||||
assert!(!check_word_mute_impl(&texts, &[], &["/QuICk/".to_string()]));
|
||||
|
||||
assert!(check_word_mute_impl(
|
||||
&texts,
|
||||
&vec![],
|
||||
&vec!["/QuICk/i".to_string()]
|
||||
));
|
||||
|
||||
assert!(!check_word_mute_impl(
|
||||
&texts,
|
||||
&vec![],
|
||||
&vec!["/QuICk/".to_string()]
|
||||
));
|
||||
|
||||
assert!(check_word_mute_impl(
|
||||
&texts,
|
||||
&vec![
|
||||
&[
|
||||
"我".to_string(),
|
||||
"有為の奥山 今日越えて 浅き夢見し 酔ひもせず".to_string()
|
||||
],
|
||||
&vec![]
|
||||
&[]
|
||||
));
|
||||
|
||||
assert!(!check_word_mute_impl(
|
||||
&texts,
|
||||
&vec!["有為の奥山 今日越えて 浅き夢見し 酔ひもせず".to_string()],
|
||||
&vec![]
|
||||
&["有為の奥山 今日越えて 浅き夢見し 酔ひもせず".to_string()],
|
||||
&[]
|
||||
));
|
||||
|
||||
assert!(!check_word_mute_impl(
|
||||
&texts,
|
||||
&vec![
|
||||
&[
|
||||
"有為の奥山".to_string(),
|
||||
"今日越えて".to_string(),
|
||||
"浅き夢見し".to_string(),
|
||||
"酔ひもせず".to_string()
|
||||
],
|
||||
&vec![]
|
||||
&[]
|
||||
));
|
||||
|
||||
assert!(check_word_mute_impl(
|
||||
&texts,
|
||||
&vec!["yellow fox".to_string(), "mastodon".to_string()],
|
||||
&vec![hiragana_1.clone()]
|
||||
&["yellow fox".to_string(), "mastodon".to_string()],
|
||||
&[hiragana_1.clone()]
|
||||
));
|
||||
|
||||
assert!(check_word_mute_impl(
|
||||
&texts,
|
||||
&vec!["yellow fox".to_string(), "mastodon".to_string()],
|
||||
&vec![hiragana_2.clone()]
|
||||
&["yellow fox".to_string(), "mastodon".to_string()],
|
||||
&[hiragana_2.clone()]
|
||||
));
|
||||
|
||||
assert!(!check_word_mute_impl(
|
||||
&texts,
|
||||
&vec!["yellow fox".to_string(), "mastodon".to_string()],
|
||||
&vec![katakana_1.clone()]
|
||||
&["yellow fox".to_string(), "mastodon".to_string()],
|
||||
&[katakana_1.clone()]
|
||||
));
|
||||
|
||||
assert!(!check_word_mute_impl(
|
||||
&texts,
|
||||
&vec!["yellow fox".to_string(), "mastodon".to_string()],
|
||||
&vec![katakana_2.clone()]
|
||||
&["yellow fox".to_string(), "mastodon".to_string()],
|
||||
&[katakana_2.clone()]
|
||||
));
|
||||
|
||||
assert!(check_word_mute_impl(
|
||||
&texts,
|
||||
&vec!["brown fox".to_string(), "mastodon".to_string()],
|
||||
&vec![katakana_1.clone()]
|
||||
&["brown fox".to_string(), "mastodon".to_string()],
|
||||
&[katakana_1.clone()]
|
||||
));
|
||||
|
||||
assert!(check_word_mute_impl(
|
||||
&texts,
|
||||
&vec!["brown fox".to_string(), "mastodon".to_string()],
|
||||
&vec![katakana_2.clone()]
|
||||
&["brown fox".to_string(), "mastodon".to_string()],
|
||||
&[katakana_2.clone()]
|
||||
));
|
||||
|
||||
assert!(check_word_mute_impl(
|
||||
&texts,
|
||||
&vec!["yellow fox".to_string(), "dog".to_string()],
|
||||
&vec![katakana_1.clone()]
|
||||
&["yellow fox".to_string(), "dog".to_string()],
|
||||
&[katakana_1.clone()]
|
||||
));
|
||||
|
||||
assert!(check_word_mute_impl(
|
||||
&texts,
|
||||
&vec!["yellow fox".to_string(), "dog".to_string()],
|
||||
&vec![katakana_2.clone()]
|
||||
&["yellow fox".to_string(), "dog".to_string()],
|
||||
&[katakana_2.clone()]
|
||||
));
|
||||
|
||||
assert!(check_word_mute_impl(
|
||||
&texts,
|
||||
&vec!["yellow fox".to_string(), "mastodon".to_string()],
|
||||
&vec![hiragana_1.clone(), katakana_1.clone()]
|
||||
&["yellow fox".to_string(), "mastodon".to_string()],
|
||||
&[hiragana_1.clone(), katakana_1.clone()]
|
||||
));
|
||||
|
||||
assert!(check_word_mute_impl(
|
||||
&texts,
|
||||
&vec!["😇".to_string(), "🥲".to_string(), "🥴".to_string()],
|
||||
&vec![]
|
||||
&["😇".to_string(), "🥲".to_string(), "🥴".to_string()],
|
||||
&[]
|
||||
));
|
||||
|
||||
assert!(!check_word_mute_impl(
|
||||
&texts,
|
||||
&vec!["🙂".to_string(), "🥲".to_string(), "🥴".to_string()],
|
||||
&vec![]
|
||||
&["🙂".to_string(), "🥲".to_string(), "🥴".to_string()],
|
||||
&[]
|
||||
));
|
||||
|
||||
assert!(check_word_mute_impl(&texts, &vec![], &vec![emoji.clone()]));
|
||||
assert!(check_word_mute_impl(&texts, &[], &[emoji.clone()]));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,10 +55,12 @@ pub async fn get_image_size_from_url(url: &str) -> Result<ImageSize, Error> {
|
|||
{
|
||||
let _ = MTX_GUARD.lock().await;
|
||||
|
||||
attempted = cache::get_one::<bool>(cache::Category::FetchUrl, url)?.is_some();
|
||||
attempted = cache::get_one::<bool>(cache::Category::FetchUrl, url)
|
||||
.await?
|
||||
.is_some();
|
||||
|
||||
if !attempted {
|
||||
cache::set_one(cache::Category::FetchUrl, url, &true, 10 * 60)?;
|
||||
cache::set_one(cache::Category::FetchUrl, url, &true, 10 * 60).await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,7 +140,7 @@ mod unit_test {
|
|||
let mp3_url = "https://firefish.dev/firefish/firefish/-/blob/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/sounds/aisha/1.mp3";
|
||||
|
||||
// delete caches in case you run this test multiple times
|
||||
cache::delete_all(cache::Category::FetchUrl).unwrap();
|
||||
cache::delete_all(cache::Category::FetchUrl).await.unwrap();
|
||||
|
||||
let png_size_1 = ImageSize {
|
||||
width: 1024,
|
||||
|
@ -207,7 +209,9 @@ mod unit_test {
|
|||
let url = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/splash.png";
|
||||
|
||||
// delete caches in case you run this test multiple times
|
||||
cache::delete_one(cache::Category::FetchUrl, url).unwrap();
|
||||
cache::delete_one(cache::Category::FetchUrl, url)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(get_image_size_from_url(url).await.is_ok());
|
||||
assert!(get_image_size_from_url(url).await.is_err());
|
||||
|
|
|
@ -46,7 +46,7 @@ async fn get_latest_version() -> Result<String, Error> {
|
|||
#[crate::export]
|
||||
pub async fn latest_version() -> Result<String, Error> {
|
||||
let version: Option<String> =
|
||||
cache::get_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL)?;
|
||||
cache::get_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL).await?;
|
||||
|
||||
if let Some(v) = version {
|
||||
tracing::trace!("use cached value: {}", v);
|
||||
|
@ -61,7 +61,8 @@ pub async fn latest_version() -> Result<String, Error> {
|
|||
UPSTREAM_PACKAGE_JSON_URL,
|
||||
&fetched_version,
|
||||
3 * 60 * 60,
|
||||
)?;
|
||||
)
|
||||
.await?;
|
||||
Ok(fetched_version)
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +98,9 @@ mod unit_test {
|
|||
#[tokio::test]
|
||||
async fn check_version() {
|
||||
// delete caches in case you run this test multiple times
|
||||
cache::delete_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL).unwrap();
|
||||
cache::delete_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// fetch from firefish.dev
|
||||
validate_version(latest_version().await.unwrap());
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "targetUserId")]
|
||||
pub target_user_id: String,
|
||||
#[sea_orm(column_name = "reporterId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
pub token: String,
|
||||
pub hash: String,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
|
@ -21,7 +21,7 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "appId")]
|
||||
pub app_id: Option<String>,
|
||||
#[sea_orm(column_name = "lastUsedAt")]
|
||||
pub last_used_at: Option<DateTime>,
|
||||
pub last_used_at: Option<DateTimeWithTimeZone>,
|
||||
pub session: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub description: Option<String>,
|
||||
|
|
|
@ -13,9 +13,9 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "expiresAt")]
|
||||
pub expires_at: DateTime,
|
||||
pub expires_at: DateTimeWithTimeZone,
|
||||
pub place: String,
|
||||
pub priority: String,
|
||||
pub url: String,
|
||||
|
|
|
@ -13,13 +13,13 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
pub text: String,
|
||||
pub title: String,
|
||||
#[sea_orm(column_name = "imageUrl")]
|
||||
pub image_url: Option<String>,
|
||||
#[sea_orm(column_name = "updatedAt")]
|
||||
pub updated_at: Option<DateTime>,
|
||||
pub updated_at: Option<DateTimeWithTimeZone>,
|
||||
#[sea_orm(column_name = "showPopup")]
|
||||
pub show_popup: bool,
|
||||
#[sea_orm(column_name = "isGoodNews")]
|
||||
|
|
|
@ -17,7 +17,7 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "announcementId")]
|
||||
pub announcement_id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
|
|
@ -14,7 +14,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
pub name: String,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: Option<String>,
|
||||
pub secret: String,
|
||||
|
|
|
@ -16,7 +16,7 @@ pub struct Model {
|
|||
pub user_id: String,
|
||||
pub challenge: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "registrationChallenge")]
|
||||
pub registration_challenge: bool,
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
pub token: String,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: Option<String>,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "blockeeId")]
|
||||
pub blockee_id: String,
|
||||
#[sea_orm(column_name = "blockerId")]
|
||||
|
|
|
@ -13,9 +13,9 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "lastNotedAt")]
|
||||
pub last_noted_at: Option<DateTime>,
|
||||
pub last_noted_at: Option<DateTimeWithTimeZone>,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: Option<String>,
|
||||
pub name: String,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "followeeId")]
|
||||
pub followee_id: String,
|
||||
#[sea_orm(column_name = "followerId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "channelId")]
|
||||
pub channel_id: String,
|
||||
#[sea_orm(column_name = "noteId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
pub name: String,
|
||||
|
|
|
@ -14,7 +14,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: Option<String>,
|
||||
#[sea_orm(column_name = "userHost")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
pub name: String,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: Option<String>,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "updatedAt")]
|
||||
pub updated_at: Option<DateTime>,
|
||||
pub updated_at: Option<DateTimeWithTimeZone>,
|
||||
pub name: String,
|
||||
pub host: Option<String>,
|
||||
#[sea_orm(column_name = "originalUrl")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "followeeId")]
|
||||
pub followee_id: String,
|
||||
#[sea_orm(column_name = "followerId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "followeeId")]
|
||||
pub followee_id: String,
|
||||
#[sea_orm(column_name = "followerId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "postId")]
|
||||
|
|
|
@ -13,9 +13,9 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "updatedAt")]
|
||||
pub updated_at: DateTime,
|
||||
pub updated_at: DateTimeWithTimeZone,
|
||||
pub title: String,
|
||||
pub description: Option<String>,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "caughtAt")]
|
||||
pub caught_at: DateTime,
|
||||
pub caught_at: DateTimeWithTimeZone,
|
||||
pub host: String,
|
||||
#[sea_orm(column_name = "usersCount")]
|
||||
pub users_count: i32,
|
||||
|
@ -24,13 +24,13 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "followersCount")]
|
||||
pub followers_count: i32,
|
||||
#[sea_orm(column_name = "latestRequestSentAt")]
|
||||
pub latest_request_sent_at: Option<DateTime>,
|
||||
pub latest_request_sent_at: Option<DateTimeWithTimeZone>,
|
||||
#[sea_orm(column_name = "latestStatus")]
|
||||
pub latest_status: Option<i32>,
|
||||
#[sea_orm(column_name = "latestRequestReceivedAt")]
|
||||
pub latest_request_received_at: Option<DateTime>,
|
||||
pub latest_request_received_at: Option<DateTimeWithTimeZone>,
|
||||
#[sea_orm(column_name = "lastCommunicatedAt")]
|
||||
pub last_communicated_at: DateTime,
|
||||
pub last_communicated_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "isNotResponding")]
|
||||
pub is_not_responding: bool,
|
||||
#[sea_orm(column_name = "softwareName")]
|
||||
|
@ -46,7 +46,7 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "maintainerEmail")]
|
||||
pub maintainer_email: Option<String>,
|
||||
#[sea_orm(column_name = "infoUpdatedAt")]
|
||||
pub info_updated_at: Option<DateTime>,
|
||||
pub info_updated_at: Option<DateTimeWithTimeZone>,
|
||||
#[sea_orm(column_name = "isSuspended")]
|
||||
pub is_suspended: bool,
|
||||
#[sea_orm(column_name = "iconUrl")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "recipientId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
pub r#type: String,
|
||||
|
|
|
@ -13,13 +13,13 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "muteeId")]
|
||||
pub mutee_id: String,
|
||||
#[sea_orm(column_name = "muterId")]
|
||||
pub muter_id: String,
|
||||
#[sea_orm(column_name = "expiresAt")]
|
||||
pub expires_at: Option<DateTime>,
|
||||
pub expires_at: Option<DateTimeWithTimeZone>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
|
|
@ -14,7 +14,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "replyId")]
|
||||
pub reply_id: Option<String>,
|
||||
#[sea_orm(column_name = "renoteId")]
|
||||
|
@ -66,7 +66,7 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "threadId")]
|
||||
pub thread_id: Option<String>,
|
||||
#[sea_orm(column_name = "updatedAt")]
|
||||
pub updated_at: Option<DateTime>,
|
||||
pub updated_at: Option<DateTimeWithTimeZone>,
|
||||
pub lang: Option<String>,
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "fileIds")]
|
||||
pub file_ids: Vec<String>,
|
||||
#[sea_orm(column_name = "updatedAt")]
|
||||
pub updated_at: DateTime,
|
||||
pub updated_at: DateTimeWithTimeZone,
|
||||
pub emojis: Vec<String>,
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "noteId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "noteId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "threadId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "noteId")]
|
||||
|
|
|
@ -14,7 +14,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "notifieeId")]
|
||||
pub notifiee_id: String,
|
||||
#[sea_orm(column_name = "notifierId")]
|
||||
|
|
|
@ -14,9 +14,9 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "updatedAt")]
|
||||
pub updated_at: DateTime,
|
||||
pub updated_at: DateTimeWithTimeZone,
|
||||
pub title: String,
|
||||
pub name: String,
|
||||
pub summary: Option<String>,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "pageId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
pub token: String,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
|
|
|
@ -14,7 +14,7 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "noteId", primary_key, auto_increment = false, unique)]
|
||||
pub note_id: String,
|
||||
#[sea_orm(column_name = "expiresAt")]
|
||||
pub expires_at: Option<DateTime>,
|
||||
pub expires_at: Option<DateTimeWithTimeZone>,
|
||||
pub multiple: bool,
|
||||
pub choices: Vec<String>,
|
||||
pub votes: Vec<i32>,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "noteId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "noteId", primary_key, auto_increment = false, unique)]
|
||||
pub note_id: String,
|
||||
#[sea_orm(column_name = "expiresAt")]
|
||||
pub expires_at: DateTime,
|
||||
pub expires_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "noteId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
pub code: String,
|
||||
}
|
||||
|
||||
|
|
|
@ -13,9 +13,9 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "updatedAt")]
|
||||
pub updated_at: DateTime,
|
||||
pub updated_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
pub key: String,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "muteeId")]
|
||||
pub mutee_id: String,
|
||||
#[sea_orm(column_name = "muterId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "muteeId")]
|
||||
pub mutee_id: String,
|
||||
#[sea_orm(column_name = "muterId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
pub ip: String,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
pub endpoint: String,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub username: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
|
|
@ -14,11 +14,11 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "updatedAt")]
|
||||
pub updated_at: Option<DateTime>,
|
||||
pub updated_at: Option<DateTimeWithTimeZone>,
|
||||
#[sea_orm(column_name = "lastFetchedAt")]
|
||||
pub last_fetched_at: Option<DateTime>,
|
||||
pub last_fetched_at: Option<DateTimeWithTimeZone>,
|
||||
pub username: String,
|
||||
#[sea_orm(column_name = "usernameLower")]
|
||||
pub username_lower: String,
|
||||
|
@ -62,7 +62,7 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "followersUri")]
|
||||
pub followers_uri: Option<String>,
|
||||
#[sea_orm(column_name = "lastActiveDate")]
|
||||
pub last_active_date: Option<DateTime>,
|
||||
pub last_active_date: Option<DateTimeWithTimeZone>,
|
||||
#[sea_orm(column_name = "hideOnlineStatus")]
|
||||
pub hide_online_status: bool,
|
||||
#[sea_orm(column_name = "isDeleted")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
pub name: String,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "userGroupId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "userGroupId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "userGroupId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
pub ip: String,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
pub name: String,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "userListId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
#[sea_orm(column_name = "noteId")]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
pub code: String,
|
||||
pub username: String,
|
||||
pub email: String,
|
||||
|
|
|
@ -17,7 +17,7 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "publicKey")]
|
||||
pub public_key: String,
|
||||
#[sea_orm(column_name = "lastUsed")]
|
||||
pub last_used: DateTime,
|
||||
pub last_used: DateTimeWithTimeZone,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct Model {
|
|||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_name = "createdAt")]
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(column_name = "userId")]
|
||||
pub user_id: String,
|
||||
pub name: String,
|
||||
|
@ -22,7 +22,7 @@ pub struct Model {
|
|||
pub secret: String,
|
||||
pub active: bool,
|
||||
#[sea_orm(column_name = "latestSentAt")]
|
||||
pub latest_sent_at: Option<DateTime>,
|
||||
pub latest_sent_at: Option<DateTimeWithTimeZone>,
|
||||
#[sea_orm(column_name = "latestStatus")]
|
||||
pub latest_status: Option<i32>,
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::database::cache;
|
||||
use crate::database::{db_conn, redis_conn, redis_key};
|
||||
use crate::database::{db_conn, redis_conn, redis_key, RedisConnError};
|
||||
use crate::federation::acct::Acct;
|
||||
use crate::misc::check_hit_antenna::{check_hit_antenna, AntennaCheckError};
|
||||
use crate::model::entity::{antenna, note};
|
||||
use crate::service::stream;
|
||||
use crate::util::id::{get_timestamp, InvalidIdErr};
|
||||
use redis::{streams::StreamMaxlen, Commands, RedisError};
|
||||
use redis::{streams::StreamMaxlen, AsyncCommands, RedisError};
|
||||
use sea_orm::{DbErr, EntityTrait};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
@ -16,6 +16,8 @@ pub enum Error {
|
|||
CacheErr(#[from] cache::Error),
|
||||
#[error("Redis error: {0}")]
|
||||
RedisErr(#[from] RedisError),
|
||||
#[error("Redis connection error: {0}")]
|
||||
RedisConnErr(#[from] RedisConnError),
|
||||
#[error("Invalid ID: {0}")]
|
||||
InvalidIdErr(#[from] InvalidIdErr),
|
||||
#[error("Stream error: {0}")]
|
||||
|
@ -33,9 +35,9 @@ type Note = note::Model;
|
|||
async fn antennas() -> Result<Vec<Antenna>, Error> {
|
||||
const CACHE_KEY: &str = "antennas";
|
||||
|
||||
Ok(cache::get::<Vec<Antenna>>(CACHE_KEY)?.unwrap_or({
|
||||
Ok(cache::get::<Vec<Antenna>>(CACHE_KEY).await?.unwrap_or({
|
||||
let antennas = antenna::Entity::find().all(db_conn().await?).await?;
|
||||
cache::set(CACHE_KEY, &antennas, 5 * 60)?;
|
||||
cache::set(CACHE_KEY, &antennas, 5 * 60).await?;
|
||||
antennas
|
||||
}))
|
||||
}
|
||||
|
@ -52,22 +54,25 @@ pub async fn update_antennas_on_new_note(
|
|||
continue;
|
||||
}
|
||||
if check_hit_antenna(antenna, note.clone(), note_author).await? {
|
||||
add_note_to_antenna(&antenna.id, ¬e)?;
|
||||
add_note_to_antenna(&antenna.id, ¬e).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_note_to_antenna(antenna_id: &str, note: &Note) -> Result<(), Error> {
|
||||
pub async fn add_note_to_antenna(antenna_id: &str, note: &Note) -> Result<(), Error> {
|
||||
// for timeline API
|
||||
redis_conn()?.xadd_maxlen(
|
||||
redis_key(format!("antennaTimeline:{}", antenna_id)),
|
||||
StreamMaxlen::Approx(200),
|
||||
format!("{}-*", get_timestamp(¬e.id)?),
|
||||
&[("note", ¬e.id)],
|
||||
)?;
|
||||
redis_conn()
|
||||
.await?
|
||||
.xadd_maxlen(
|
||||
redis_key(format!("antennaTimeline:{}", antenna_id)),
|
||||
StreamMaxlen::Approx(200),
|
||||
format!("{}-*", get_timestamp(¬e.id)?),
|
||||
&[("note", ¬e.id)],
|
||||
)
|
||||
.await?;
|
||||
|
||||
// for streaming API
|
||||
Ok(stream::antenna::publish(antenna_id.to_string(), note)?)
|
||||
Ok(stream::antenna::publish(antenna_id.to_string(), note).await?)
|
||||
}
|
||||
|
|
|
@ -116,13 +116,13 @@ async fn generate_nodeinfo_2_1() -> Result<Nodeinfo21, Error> {
|
|||
pub async fn nodeinfo_2_1() -> Result<Nodeinfo21, Error> {
|
||||
const NODEINFO_2_1_CACHE_KEY: &str = "nodeinfo_2_1";
|
||||
|
||||
let cached = cache::get::<Nodeinfo21>(NODEINFO_2_1_CACHE_KEY)?;
|
||||
let cached = cache::get::<Nodeinfo21>(NODEINFO_2_1_CACHE_KEY).await?;
|
||||
|
||||
if let Some(nodeinfo) = cached {
|
||||
Ok(nodeinfo)
|
||||
} else {
|
||||
let nodeinfo = generate_nodeinfo_2_1().await?;
|
||||
cache::set(NODEINFO_2_1_CACHE_KEY, &nodeinfo, 60 * 60)?;
|
||||
cache::set(NODEINFO_2_1_CACHE_KEY, &nodeinfo, 60 * 60).await?;
|
||||
Ok(nodeinfo)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ pub async fn watch_note(
|
|||
if watcher_id != note_author_id {
|
||||
note_watching::Entity::insert(note_watching::ActiveModel {
|
||||
id: ActiveValue::set(gen_id()),
|
||||
created_at: ActiveValue::set(chrono::Local::now().naive_local()),
|
||||
created_at: ActiveValue::set(chrono::Utc::now().into()),
|
||||
user_id: ActiveValue::Set(watcher_id.to_string()),
|
||||
note_user_id: ActiveValue::Set(note_author_id.to_string()),
|
||||
note_id: ActiveValue::Set(note_id.to_string()),
|
||||
|
|
|
@ -47,6 +47,7 @@ pub enum PushNotificationKind {
|
|||
ReadNotifications,
|
||||
#[strum(serialize = "readAllNotifications")]
|
||||
ReadAllNotifications,
|
||||
Mastodon,
|
||||
}
|
||||
|
||||
fn compact_content(
|
||||
|
@ -158,15 +159,29 @@ pub async fn send_push_notification(
|
|||
.all(db)
|
||||
.await?;
|
||||
|
||||
let payload = format!(
|
||||
"{{\"type\":\"{}\",\"userId\":\"{}\",\"dateTime\":{},\"body\":{}}}",
|
||||
kind,
|
||||
receiver_user_id,
|
||||
chrono::Utc::now().timestamp_millis(),
|
||||
serde_json::to_string(&compact_content(&kind, content.clone())?)?
|
||||
);
|
||||
// TODO: refactoring
|
||||
let payload = if kind == PushNotificationKind::Mastodon {
|
||||
// Leave the `content` as it is
|
||||
serde_json::to_string(content)?
|
||||
} else {
|
||||
// Format the `content` passed from the TypeScript backend
|
||||
// for Firefish push notifications
|
||||
format!(
|
||||
"{{\"type\":\"{}\",\"userId\":\"{}\",\"dateTime\":{},\"body\":{}}}",
|
||||
kind,
|
||||
receiver_user_id,
|
||||
chrono::Utc::now().timestamp_millis(),
|
||||
serde_json::to_string(&compact_content(&kind, content.clone())?)?
|
||||
)
|
||||
};
|
||||
tracing::trace!("payload: {:#?}", payload);
|
||||
|
||||
let encoding = if kind == PushNotificationKind::Mastodon {
|
||||
ContentEncoding::AesGcm
|
||||
} else {
|
||||
ContentEncoding::Aes128Gcm
|
||||
};
|
||||
|
||||
for subscription in subscriptions.iter() {
|
||||
if !subscription.send_read_message
|
||||
&& [
|
||||
|
@ -211,7 +226,7 @@ pub async fn send_push_notification(
|
|||
|
||||
let mut message_builder = WebPushMessageBuilder::new(&subscription_info);
|
||||
message_builder.set_ttl(1000);
|
||||
message_builder.set_payload(ContentEncoding::Aes128Gcm, payload.as_bytes());
|
||||
message_builder.set_payload(encoding, payload.as_bytes());
|
||||
message_builder.set_vapid_signature(signature.unwrap());
|
||||
|
||||
let message = message_builder.build();
|
||||
|
|
|
@ -7,8 +7,8 @@ pub mod group_chat;
|
|||
pub mod moderation;
|
||||
|
||||
use crate::config::CONFIG;
|
||||
use crate::database::redis_conn;
|
||||
use redis::{Commands, RedisError};
|
||||
use crate::database::{redis_conn, RedisConnError};
|
||||
use redis::{AsyncCommands, RedisError};
|
||||
|
||||
#[derive(strum::Display)]
|
||||
pub enum Stream {
|
||||
|
@ -49,13 +49,15 @@ pub enum Stream {
|
|||
pub enum Error {
|
||||
#[error("Redis error: {0}")]
|
||||
RedisError(#[from] RedisError),
|
||||
#[error("Redis connection error: {0}")]
|
||||
RedisConnErr(#[from] RedisConnError),
|
||||
#[error("Json (de)serialization error: {0}")]
|
||||
JsonError(#[from] serde_json::Error),
|
||||
#[error("Value error: {0}")]
|
||||
ValueError(String),
|
||||
}
|
||||
|
||||
pub fn publish_to_stream(
|
||||
pub async fn publish_to_stream(
|
||||
stream: &Stream,
|
||||
kind: Option<String>,
|
||||
value: Option<String>,
|
||||
|
@ -70,10 +72,13 @@ pub fn publish_to_stream(
|
|||
value.ok_or(Error::ValueError("Invalid streaming message".to_string()))?
|
||||
};
|
||||
|
||||
redis_conn()?.publish(
|
||||
&CONFIG.host,
|
||||
format!("{{\"channel\":\"{}\",\"message\":{}}}", stream, message),
|
||||
)?;
|
||||
redis_conn()
|
||||
.await?
|
||||
.publish(
|
||||
&CONFIG.host,
|
||||
format!("{{\"channel\":\"{}\",\"message\":{}}}", stream, message),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::model::entity::note;
|
||||
use crate::service::stream::{publish_to_stream, Error, Stream};
|
||||
|
||||
pub fn publish(antenna_id: String, note: ¬e::Model) -> Result<(), Error> {
|
||||
pub async fn publish(antenna_id: String, note: ¬e::Model) -> Result<(), Error> {
|
||||
publish_to_stream(
|
||||
&Stream::Antenna { antenna_id },
|
||||
Some("note".to_string()),
|
||||
Some(serde_json::to_string(note)?),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::service::stream::{publish_to_stream, Error, Stream};
|
||||
|
||||
#[crate::export(js_name = "publishToChannelStream")]
|
||||
pub fn publish(channel_id: String, user_id: String) -> Result<(), Error> {
|
||||
pub async fn publish(channel_id: String, user_id: String) -> Result<(), Error> {
|
||||
publish_to_stream(
|
||||
&Stream::Channel { channel_id },
|
||||
Some("typing".to_string()),
|
||||
Some(format!("\"{}\"", user_id)),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ pub enum ChatEvent {
|
|||
// https://github.com/napi-rs/napi-rs/issues/2036
|
||||
|
||||
#[crate::export(js_name = "publishToChatStream")]
|
||||
pub fn publish(
|
||||
pub async fn publish(
|
||||
sender_user_id: String,
|
||||
receiver_user_id: String,
|
||||
kind: ChatEvent,
|
||||
|
@ -31,4 +31,5 @@ pub fn publish(
|
|||
Some(kind.to_string()),
|
||||
Some(serde_json::to_string(object)?),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ pub enum ChatIndexEvent {
|
|||
// https://github.com/napi-rs/napi-rs/issues/2036
|
||||
|
||||
#[crate::export(js_name = "publishToChatIndexStream")]
|
||||
pub fn publish(
|
||||
pub async fn publish(
|
||||
user_id: String,
|
||||
kind: ChatIndexEvent,
|
||||
object: &serde_json::Value,
|
||||
|
@ -23,4 +23,5 @@ pub fn publish(
|
|||
Some(kind.to_string()),
|
||||
Some(serde_json::to_string(object)?),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -18,10 +18,11 @@ pub struct PackedEmoji {
|
|||
}
|
||||
|
||||
#[crate::export(js_name = "publishToBroadcastStream")]
|
||||
pub fn publish(emoji: &PackedEmoji) -> Result<(), Error> {
|
||||
pub async fn publish(emoji: &PackedEmoji) -> Result<(), Error> {
|
||||
publish_to_stream(
|
||||
&Stream::CustomEmoji,
|
||||
Some("emojiAdded".to_string()),
|
||||
Some(format!("{{\"emoji\":{}}}", serde_json::to_string(emoji)?)),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -4,10 +4,15 @@ use crate::service::stream::{chat::ChatEvent, publish_to_stream, Error, Stream};
|
|||
// https://github.com/napi-rs/napi-rs/issues/2036
|
||||
|
||||
#[crate::export(js_name = "publishToGroupChatStream")]
|
||||
pub fn publish(group_id: String, kind: ChatEvent, object: &serde_json::Value) -> Result<(), Error> {
|
||||
pub async fn publish(
|
||||
group_id: String,
|
||||
kind: ChatEvent,
|
||||
object: &serde_json::Value,
|
||||
) -> Result<(), Error> {
|
||||
publish_to_stream(
|
||||
&Stream::GroupChat { group_id },
|
||||
Some(kind.to_string()),
|
||||
Some(serde_json::to_string(object)?),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -12,10 +12,11 @@ pub struct AbuseUserReportLike {
|
|||
}
|
||||
|
||||
#[crate::export(js_name = "publishToModerationStream")]
|
||||
pub fn publish(moderator_id: String, report: &AbuseUserReportLike) -> Result<(), Error> {
|
||||
pub async fn publish(moderator_id: String, report: &AbuseUserReportLike) -> Result<(), Error> {
|
||||
publish_to_stream(
|
||||
&Stream::Moderation { moderator_id },
|
||||
Some("newAbuseUserReport".to_string()),
|
||||
Some(serde_json::to_string(report)?),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -40,11 +40,10 @@
|
|||
"axios": "1.6.8",
|
||||
"backend-rs": "workspace:*",
|
||||
"blurhash": "2.0.5",
|
||||
"bull": "4.12.5",
|
||||
"bull": "4.12.6",
|
||||
"cacheable-lookup": "TheEssem/cacheable-lookup",
|
||||
"cbor-x": "1.5.9",
|
||||
"chalk": "5.3.0",
|
||||
"chalk-template": "1.1.0",
|
||||
"cli-highlight": "2.1.11",
|
||||
"color-convert": "2.0.1",
|
||||
"content-disposition": "0.5.4",
|
||||
|
@ -56,9 +55,9 @@
|
|||
"feed": "4.2.2",
|
||||
"file-type": "19.0.0",
|
||||
"firefish-js": "workspace:*",
|
||||
"fluent-ffmpeg": "2.1.2",
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"form-data": "4.0.0",
|
||||
"got": "14.2.1",
|
||||
"got": "14.3.0",
|
||||
"gunzip-maybe": "1.4.2",
|
||||
"hpagent": "1.2.0",
|
||||
"ioredis": "5.4.1",
|
||||
|
@ -169,7 +168,7 @@
|
|||
"@types/websocket": "1.0.10",
|
||||
"@types/ws": "8.5.10",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "9.2.0",
|
||||
"eslint": "9.3.0",
|
||||
"mocha": "10.4.0",
|
||||
"pug": "3.0.2",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
type DateTimeWithTimeZone = Date;
|
|
@ -1,15 +1,11 @@
|
|||
import * as fs from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname } from "node:path";
|
||||
import * as os from "node:os";
|
||||
import cluster from "node:cluster";
|
||||
import chalk from "chalk";
|
||||
import chalkTemplate from "chalk-template";
|
||||
import semver from "semver";
|
||||
|
||||
import Logger from "@/services/logger.js";
|
||||
import {
|
||||
fetchMeta,
|
||||
greet,
|
||||
initializeRustLogger,
|
||||
removeOldAttestationChallenges,
|
||||
showServerInfo,
|
||||
|
@ -19,74 +15,9 @@ import { config, envOption } from "@/config.js";
|
|||
import { db, initDb } from "@/db/postgre.js";
|
||||
import { inspect } from "node:util";
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
const meta = JSON.parse(
|
||||
fs.readFileSync(`${_dirname}/../../../../built/meta.json`, "utf-8"),
|
||||
);
|
||||
|
||||
const logger = new Logger("core", "cyan");
|
||||
const bootLogger = logger.createSubLogger("boot", "magenta", false);
|
||||
|
||||
const themeColor = chalk.hex("#31748f");
|
||||
|
||||
function greet() {
|
||||
//#region Firefish logo
|
||||
console.log(
|
||||
themeColor(
|
||||
"██████╗ ██╗██████╗ ███████╗███████╗██╗███████╗██╗ ██╗ ○ ▄ ▄ ",
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
themeColor(
|
||||
"██╔════╝██║██╔══██╗██╔════╝██╔════╝██║██╔════╝██║ ██║ ⚬ █▄▄ █▄▄ ",
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
themeColor(
|
||||
"█████╗ ██║██████╔╝█████╗ █████╗ ██║███████╗███████║ ▄▄▄▄▄▄ ▄ ",
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
themeColor(
|
||||
"██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║╚════██║██╔══██║ █ █ █▄▄ ",
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
themeColor(
|
||||
"██║ ██║██║ ██║███████╗██║ ██║███████║██║ ██║ █ ● ● █ ",
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
themeColor(
|
||||
"╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ▀▄▄▄▄▄▄▀ ",
|
||||
),
|
||||
);
|
||||
//#endregion
|
||||
|
||||
console.log(
|
||||
" Firefish is an open-source decentralized microblogging platform.",
|
||||
);
|
||||
console.log(
|
||||
chalk.rgb(
|
||||
255,
|
||||
136,
|
||||
0,
|
||||
)(
|
||||
" If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish",
|
||||
),
|
||||
);
|
||||
|
||||
console.log("");
|
||||
console.log(
|
||||
chalkTemplate`--- ${os.hostname()} {gray (PID: ${process.pid.toString()})} ---`,
|
||||
);
|
||||
|
||||
bootLogger.info("Welcome to Firefish!");
|
||||
bootLogger.info(`Firefish v${meta.version}`, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init master process
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,459 @@
|
|||
import type { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class AddBackTimezone1715351290096 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "abuse_user_report" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "access_token" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "access_token" ALTER "lastUsedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "ad" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "ad" ALTER "expiresAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "announcement" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "announcement" ALTER "updatedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "announcement_read" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "app" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "attestation_challenge" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "auth_session" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "blocking" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "channel" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "channel" ALTER "lastNotedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "channel_following" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "channel_note_pining" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "clip" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "drive_file" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "drive_folder" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "emoji" ALTER "updatedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "following" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "follow_request" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "gallery_like" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "gallery_post" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "gallery_post" ALTER "updatedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER "caughtAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER "infoUpdatedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER "lastCommunicatedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER "latestRequestReceivedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER "latestRequestSentAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "messaging_message" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "moderation_log" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "muting" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "muting" ALTER "expiresAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note" ALTER "updatedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note_edit" ALTER "updatedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note_favorite" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note_reaction" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note_thread_muting" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note_watching" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "notification" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "page" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "page" ALTER "updatedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "page_like" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "password_reset_request" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "poll" ALTER "expiresAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "poll_vote" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "promo_note" ALTER "expiresAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "promo_read" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "registration_ticket" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "registry_item" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "registry_item" ALTER "updatedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "renote_muting" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "reply_muting" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "signin" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "sw_subscription" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "used_username" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" ALTER "lastActiveDate" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" ALTER "lastFetchedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" ALTER "updatedAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_group" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_group_invitation" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_group_invite" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_group_joining" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_ip" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_list" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_list_joining" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_note_pining" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_pending" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_security_key" ALTER "lastUsed" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "webhook" ALTER "createdAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "webhook" ALTER "latestSentAt" TYPE timestamp with time zone`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "abuse_user_report" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "access_token" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "access_token" ALTER "lastUsedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "ad" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "ad" ALTER "expiresAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "announcement" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "announcement" ALTER "updatedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "announcement_read" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "app" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "attestation_challenge" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "auth_session" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "blocking" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "channel" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "channel" ALTER "lastNotedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "channel_following" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "channel_note_pining" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "clip" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "drive_file" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "drive_folder" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "emoji" ALTER "updatedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "following" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "follow_request" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "gallery_like" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "gallery_post" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "gallery_post" ALTER "updatedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER "caughtAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER "infoUpdatedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER "lastCommunicatedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER "latestRequestReceivedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER "latestRequestSentAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "messaging_message" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "moderation_log" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "muting" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "muting" ALTER "expiresAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note" ALTER "updatedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note_edit" ALTER "updatedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note_favorite" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note_reaction" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note_thread_muting" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note_watching" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "notification" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "page" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "page" ALTER "updatedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "page_like" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "password_reset_request" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "poll" ALTER "expiresAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "poll_vote" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "promo_note" ALTER "expiresAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "promo_read" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "registration_ticket" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "registry_item" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "registry_item" ALTER "updatedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "renote_muting" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "reply_muting" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "signin" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "sw_subscription" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "used_username" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" ALTER "lastActiveDate" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" ALTER "lastFetchedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" ALTER "updatedAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_group" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_group_invitation" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_group_invite" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_group_joining" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_ip" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_list" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_list_joining" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_note_pining" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_pending" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_security_key" ALTER "lastUsed" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "webhook" ALTER "createdAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "webhook" ALTER "latestSentAt" TYPE timestamp without time zone`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ export class AbuseUserReport {
|
|||
public id: string;
|
||||
|
||||
@Index()
|
||||
@Column("timestamp without time zone", {
|
||||
@Column("timestamp with time zone", {
|
||||
comment: "The created date of the AbuseUserReport.",
|
||||
})
|
||||
public createdAt: Date;
|
||||
|
|
|
@ -16,12 +16,12 @@ export class AccessToken {
|
|||
@PrimaryColumn(id())
|
||||
public id: string;
|
||||
|
||||
@Column("timestamp without time zone", {
|
||||
@Column("timestamp with time zone", {
|
||||
comment: "The created date of the AccessToken.",
|
||||
})
|
||||
public createdAt: Date;
|
||||
|
||||
@Column("timestamp without time zone", {
|
||||
@Column("timestamp with time zone", {
|
||||
nullable: true,
|
||||
})
|
||||
public lastUsedAt: Date | null;
|
||||
|
|
|
@ -7,13 +7,13 @@ export class Ad {
|
|||
public id: string;
|
||||
|
||||
@Index()
|
||||
@Column("timestamp without time zone", {
|
||||
@Column("timestamp with time zone", {
|
||||
comment: "The created date of the Ad.",
|
||||
})
|
||||
public createdAt: Date;
|
||||
|
||||
@Index()
|
||||
@Column("timestamp without time zone", {
|
||||
@Column("timestamp with time zone", {
|
||||
comment: "The expired date of the Ad.",
|
||||
})
|
||||
public expiresAt: Date;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue