Merge branch 'develop' into 'main'

release: v20240728

Co-authored-by: 老周部落 <laozhoubuluo@gmail.com>
Co-authored-by: GitLab CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!11211
This commit is contained in:
naskya 2024-07-27 16:28:46 +00:00
commit 22b89fe3bd
185 changed files with 2004 additions and 1823 deletions

View file

@ -40,6 +40,7 @@ packages/backend/assets/instance.css
.gitattributes .gitattributes
.weblate .weblate
animated.svg animated.svg
compose.yml
docker-compose.yml docker-compose.yml
docker-compose.example.yml docker-compose.example.yml
title.svg title.svg
@ -48,6 +49,7 @@ title.svg
/dev /dev
/docs /docs
/scripts /scripts
!/scripts/copy-index.mjs
!/scripts/copy-assets.mjs !/scripts/copy-assets.mjs
biome.json biome.json
CODE_OF_CONDUCT.md CODE_OF_CONDUCT.md

View file

@ -15,7 +15,7 @@
"biomejs.biome", "biomejs.biome",
"rust-lang.rust-analyzer", "rust-lang.rust-analyzer",
"vadimcn.vscode-lldb", "vadimcn.vscode-lldb",
"serayuzgur.crates", "fill-labs.dependi",
"tamasfe.even-better-toml", "tamasfe.even-better-toml",
"aaron-bond.better-comments" "aaron-bond.better-comments"
] ]

63
Cargo.lock generated
View file

@ -84,6 +84,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
[[package]]
name = "arc-swap"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]] [[package]]
name = "arg_enum_proc_macro" name = "arg_enum_proc_macro"
version = "0.3.4" version = "0.3.4"
@ -871,9 +877,9 @@ dependencies = [
[[package]] [[package]]
name = "emojis" name = "emojis"
version = "0.6.2" version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f619a926616ae7149a0d82610b051134a0d6c4ae2962d990c06c847a445c5d9" checksum = "e72f23d65b46527e461b161ab9a126c378aa2249d8a8d15718d23ab1fb4d8786"
dependencies = [ dependencies = [
"phf", "phf",
] ]
@ -1576,9 +1582,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]] [[package]]
name = "jobserver" name = "jobserver"
version = "0.1.31" version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -1768,6 +1774,8 @@ dependencies = [
"quote", "quote",
"serde", "serde",
"serde_json", "serde_json",
"syn 2.0.72",
"thiserror",
] ]
[[package]] [[package]]
@ -1919,14 +1927,15 @@ dependencies = [
[[package]] [[package]]
name = "nom-exif" name = "nom-exif"
version = "1.2.6" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dac386e49252f313c88764101ca16681caa4a5b0260d494d58b6af7f04edd3a" checksum = "ab35c38930e837f191de96c6f23e8dee7547916fbae0ce25e4d5fad405bbc2d2"
dependencies = [ dependencies = [
"chrono", "chrono",
"nom", "nom",
"regex", "regex",
"thiserror", "thiserror",
"tracing",
] ]
[[package]] [[package]]
@ -2073,9 +2082,9 @@ dependencies = [
[[package]] [[package]]
name = "object" name = "object"
version = "0.36.1" version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@ -2621,15 +2630,17 @@ dependencies = [
[[package]] [[package]]
name = "redis" name = "redis"
version = "0.25.4" version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0d7a6955c7511f60f3ba9e86c6d02b3c3f144f8c24b288d1f4e18074ab8bbec" checksum = "8cc5b667390cb038bc65fc4b18c06e2550469f7e06a02d886f1a018a11f63563"
dependencies = [ dependencies = [
"arc-swap",
"async-trait", "async-trait",
"bytes", "bytes",
"combine", "combine",
"futures-util", "futures-util",
"itoa", "itoa",
"num-bigint",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"ryu", "ryu",
@ -3021,9 +3032,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "0.6.6" version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -3676,9 +3687,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.39.1" version = "1.39.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@ -3741,9 +3752,9 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.8.15" version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c"
dependencies = [ dependencies = [
"serde", "serde",
"serde_spanned", "serde_spanned",
@ -3753,18 +3764,18 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.6" version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.22.16" version = "0.22.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde",
@ -3975,9 +3986,9 @@ checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "waker-fn" name = "waker-fn"
@ -4285,9 +4296,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.6.14" version = "0.6.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f" checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@ -4461,9 +4472,9 @@ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]] [[package]]
name = "zune-jpeg" name = "zune-jpeg"
version = "0.4.11" version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768"
dependencies = [ dependencies = [
"zune-core", "zune-core",
] ]

View file

@ -22,17 +22,17 @@ bcrypt = { version = "0.15.1", default-features = false }
chrono = { version = "0.4.38", default-features = false } chrono = { version = "0.4.38", default-features = false }
convert_case = { version = "0.6.0", default-features = false } convert_case = { version = "0.6.0", default-features = false }
cuid2 = { version = "0.1.2", default-features = false } cuid2 = { version = "0.1.2", default-features = false }
emojis = { version = "0.6.2", default-features = false } emojis = { version = "0.6.3", default-features = false }
idna = { version = "1.0.2", default-features = false } idna = { version = "1.0.2", default-features = false }
image = { version = "0.25.2", default-features = false } image = { version = "0.25.2", default-features = false }
isahc = { version = "1.7.2", default-features = false } isahc = { version = "1.7.2", default-features = false }
nom-exif = { version = "1.2.6", default-features = false } nom-exif = { version = "1.3.0", default-features = false }
once_cell = { version = "1.19.0", default-features = false } once_cell = { version = "1.19.0", default-features = false }
pretty_assertions = { version = "1.4.0", default-features = false } pretty_assertions = { version = "1.4.0", default-features = false }
proc-macro2 = { version = "1.0.86", default-features = false } proc-macro2 = { version = "1.0.86", default-features = false }
quote = { version = "1.0.36", default-features = false } quote = { version = "1.0.36", default-features = false }
rand = { version = "0.8.5", default-features = false } rand = { version = "0.8.5", default-features = false }
redis = { version = "0.25.4", default-features = false } redis = { version = "0.26.0", default-features = false }
regex = { version = "1.10.5", default-features = false } regex = { version = "1.10.5", default-features = false }
rmp-serde = { version = "1.3.0", default-features = false } rmp-serde = { version = "1.3.0", default-features = false }
sea-orm = { version = "0.12.15", default-features = false } sea-orm = { version = "0.12.15", default-features = false }
@ -42,7 +42,7 @@ serde_yaml = { version = "0.9.34", default-features = false }
syn = { version = "2.0.72", default-features = false } syn = { version = "2.0.72", default-features = false }
sysinfo = { version = "0.30.13", default-features = false } sysinfo = { version = "0.30.13", default-features = false }
thiserror = { version = "1.0.63", default-features = false } thiserror = { version = "1.0.63", default-features = false }
tokio = { version = "1.39.1", default-features = false } tokio = { version = "1.39.2", default-features = false }
tokio-test = { version = "0.4.4", default-features = false } tokio-test = { version = "0.4.4", default-features = false }
tracing = { version = "0.1.40", default-features = false } tracing = { version = "0.1.40", default-features = false }
tracing-subscriber = { version = "0.3.18", default-features = false } tracing-subscriber = { version = "0.3.18", default-features = false }

View file

@ -1,56 +1,28 @@
## Install dev and compilation dependencies, build files # Install dev and compilation dependencies, build files
FROM docker.io/node:20-alpine as build FROM docker.io/node:20-alpine AS build
WORKDIR /firefish WORKDIR /firefish
# Copy only backend-rs pnpm-related files first, to cache efficiently # Install build tools and work around the linker name issue
COPY package.json pnpm-workspace.yaml ./
COPY packages/backend-rs/package.json packages/backend-rs/package.json
# Install compilation dependencies
RUN apk update && apk add --no-cache build-base linux-headers curl ca-certificates python3 perl RUN apk update && apk add --no-cache build-base linux-headers curl ca-certificates python3 perl
RUN ln -s $(which gcc) /usr/bin/aarch64-linux-musl-gcc
# Install Rust toolchain
RUN curl --proto '=https' --tlsv1.2 --silent --show-error --fail https://sh.rustup.rs | sh -s -- -y RUN curl --proto '=https' --tlsv1.2 --silent --show-error --fail https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}" ENV PATH="/root/.cargo/bin:${PATH}"
# Copy only backend-rs dependency-related files first, to cache efficiently # Configure pnpm
COPY packages/macro-rs packages/macro-rs/ RUN corepack enable && corepack prepare pnpm@latest --activate
COPY packages/backend-rs/src/lib.rs packages/backend-rs/src/
COPY packages/backend-rs/Cargo.toml packages/backend-rs/Cargo.toml
COPY Cargo.toml Cargo.lock ./
# Configure pnpm, and install backend-rs dependencies # Build
RUN corepack enable && corepack prepare pnpm@latest --activate && pnpm --filter backend-rs install
RUN cargo fetch --locked --manifest-path Cargo.toml
# Copy in the rest of the rust files
COPY packages/backend-rs packages/backend-rs/
# Compile backend-rs
RUN ln -s $(which gcc) /usr/bin/aarch64-linux-musl-gcc
RUN NODE_ENV='production' NODE_OPTIONS='--max_old_space_size=3072' pnpm run --filter backend-rs build
# Copy/Overwrite index.js to mitigate the bug in napi-rs codegen
COPY packages/backend-rs/index.js packages/backend-rs/built/index.js
# Copy only the dependency-related files first, to cache efficiently
COPY packages/backend/package.json packages/backend/package.json
COPY packages/client/package.json packages/client/package.json
COPY packages/sw/package.json packages/sw/package.json
COPY packages/firefish-js/package.json packages/firefish-js/package.json
COPY pnpm-lock.yaml ./
# Install dev mode dependencies for compilation
RUN pnpm install --frozen-lockfile
# Copy in the rest of the files to build
COPY . ./ COPY . ./
RUN pnpm install --frozen-lockfile
# Build other workspaces RUN cargo fetch --locked --manifest-path Cargo.toml
RUN NODE_ENV='production' NODE_OPTIONS='--max_old_space_size=3072' pnpm run --recursive --filter '!backend-rs' build && pnpm run build:assets RUN NODE_ENV='production' NODE_OPTIONS='--max_old_space_size=3072' pnpm run build
# Trim down the dependencies to only those for production # Trim down the dependencies to only those for production
RUN find . -path '*/node_modules/*' -delete && pnpm install --prod --frozen-lockfile RUN find . -path '*/node_modules/*' -delete && pnpm install --prod --frozen-lockfile
## Runtime container # Runtime container
FROM docker.io/node:20-alpine FROM docker.io/node:20-alpine
WORKDIR /firefish WORKDIR /firefish
@ -66,7 +38,7 @@ COPY --from=build /firefish/packages/backend/node_modules /firefish/packages/bac
# COPY --from=build /firefish/packages/client/node_modules /firefish/packages/client/node_modules # COPY --from=build /firefish/packages/client/node_modules /firefish/packages/client/node_modules
COPY --from=build /firefish/packages/firefish-js/node_modules /firefish/packages/firefish-js/node_modules COPY --from=build /firefish/packages/firefish-js/node_modules /firefish/packages/firefish-js/node_modules
# Copy the finished compiled files # Copy the build artifacts
COPY --from=build /firefish/built /firefish/built COPY --from=build /firefish/built /firefish/built
COPY --from=build /firefish/packages/backend/built /firefish/packages/backend/built COPY --from=build /firefish/packages/backend/built /firefish/packages/backend/built
COPY --from=build /firefish/packages/backend/assets/instance.css /firefish/packages/backend/assets/instance.css COPY --from=build /firefish/packages/backend/assets/instance.css /firefish/packages/backend/assets/instance.css

View file

@ -2,6 +2,11 @@
Breaking changes are indicated by the :warning: icon. Breaking changes are indicated by the :warning: icon.
## v20240728
- Added `name`, `category`, `aliases`, `license` optional parameters to `admin/emoji/add` endpoint.
- Added `name` optional parameter to `drive/files/upload-from-url` endpoint.
## v20240725 ## v20240725
- Added `i/export-followers` endpoint. - Added `i/export-followers` endpoint.

View file

@ -2,9 +2,16 @@
Critical security updates are indicated by the :warning: icon. Critical security updates are indicated by the :warning: icon.
This changelog is not an exhaustive list. Code refactorings, minor bug fixes, documentation/dependency updates, etc. are usually not listed here. If you want to see all changes, click on the version number and check the commit history.
- Server administrators must check [notice-for-admins.md](https://firefish.dev/firefish/firefish/-/blob/main/docs/notice-for-admins.md) as well. - Server administrators must check [notice-for-admins.md](https://firefish.dev/firefish/firefish/-/blob/main/docs/notice-for-admins.md) as well.
- Third-party client/bot developers may want to check [api-change.md](https://firefish.dev/firefish/firefish/-/blob/main/docs/api-change.md) as well. - Third-party client/bot developers may want to check [api-change.md](https://firefish.dev/firefish/firefish/-/blob/main/docs/api-change.md) as well.
## [v20240728](https://firefish.dev/firefish/firefish/-/merge_requests/11211/commits)
- Improve `admin/emoji/add` API
- Fix bugs
## [v20240725](https://firefish.dev/firefish/firefish/-/merge_requests/11196/commits) ## [v20240725](https://firefish.dev/firefish/firefish/-/merge_requests/11196/commits)
- Add followers list export feature - Add followers list export feature

View file

@ -1,8 +1,12 @@
# Install Firefish # Install Firefish
Please check the [v20240206 release note](https://firefish.dev/firefish/firefish/-/releases/v20240206) first. This project is barely maintained for those who really want to keep using Firefish. Please understand this before proceeding.
## Dependencies
Firefish depends on the following software. Firefish depends on the following software.
## Runtime dependencies ### Runtime dependencies
- At least [NodeJS](https://nodejs.org/en/) v18.19.0 (v20/v22 recommended) - At least [NodeJS](https://nodejs.org/en/) v18.19.0 (v20/v22 recommended)
- At least [PostgreSQL](https://www.postgresql.org/) v12 (v16 recommended) with [PGroonga](https://pgroonga.github.io/) extension - At least [PostgreSQL](https://www.postgresql.org/) v12 (v16 recommended) with [PGroonga](https://pgroonga.github.io/) extension
@ -17,7 +21,7 @@ Firefish depends on the following software.
- [KeyDB](https://keydb.dev/) - [KeyDB](https://keydb.dev/)
- Another [Redis](https://redis.io/) / [Valkey](https://valkey.io/) server - Another [Redis](https://redis.io/) / [Valkey](https://valkey.io/) server
## Build dependencies ### Build dependencies
- At least [Rust](https://www.rust-lang.org/) v1.74 - At least [Rust](https://www.rust-lang.org/) v1.74
- C/C++ compiler & build tools (like [GNU Make](https://www.gnu.org/software/make/)) - C/C++ compiler & build tools (like [GNU Make](https://www.gnu.org/software/make/))
@ -35,7 +39,7 @@ We don't test Firefish on non-Linux systems, so please install Firefish on such
<details> <details>
<summary>Possible setup on FreeBSD (as of version <code>20240630</code>)</summary> <summary>Possible setup on FreeBSD (as of version <code>20240725</code>)</summary>
You can install Firefish on FreeBSD by adding these extra steps to the standard instructions: You can install Firefish on FreeBSD by adding these extra steps to the standard instructions:
@ -44,7 +48,7 @@ You can install Firefish on FreeBSD by adding these extra steps to the standard
```json ```json
"pnpm": { "pnpm": {
"overrides": { "overrides": {
"rollup": "npm:@rollup/wasm-node@4.17.2" "rollup": "npm:@rollup/wasm-node
} }
} }
``` ```

View file

@ -1,6 +1,6 @@
{ {
"name": "firefish", "name": "firefish",
"version": "20240725", "version": "20240728",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://firefish.dev/firefish/firefish.git" "url": "https://firefish.dev/firefish/firefish.git"

View file

@ -48,6 +48,8 @@ export interface Acct {
export declare function acctToString(acct: Acct): string export declare function acctToString(acct: Acct): string
export type Activity = 'Follow';
export interface Ad { export interface Ad {
id: string id: string
createdAt: DateTimeWithTimeZone createdAt: DateTimeWithTimeZone
@ -217,7 +219,7 @@ export interface Config {
proxySmtp?: string proxySmtp?: string
proxyBypassHosts?: Array<string> proxyBypassHosts?: Array<string>
allowedPrivateNetworks?: Array<string> allowedPrivateNetworks?: Array<string>
maxFileSize?: number maxFileSize: number
accessLog?: string accessLog?: string
clusterLimits: WorkerConfig clusterLimits: WorkerConfig
cuid?: IdConfig cuid?: IdConfig
@ -369,6 +371,40 @@ export interface Emoji {
height: number | null height: number | null
} }
export declare enum Event {
Notification = 0,
NewNotification = 1,
Mention = 2,
NewMention = 3,
Chat = 4,
NewChat = 5,
NewDm = 6,
Reply = 7,
Renote = 8,
Follow = 9,
Followed = 10,
Unfollow = 11,
NewFollowRequest = 12,
Page = 13,
ReadAllNotifications = 14,
ReadAllMentions = 15,
ReadNotifications = 16,
ReadAllDms = 17,
ReadAllChats = 18,
ReadAntenna = 19,
ReadAllAntennaPosts = 20,
NewAntennaPost = 21,
ReadAllAnnouncements = 22,
ReadAllChannelPosts = 23,
NewChannelPost = 24,
DriveFile = 25,
UrlUploadFinished = 26,
Me = 27,
RegenerateMyToken = 28,
Signin = 29,
Registry = 30
}
export declare function extractHost(uri: string): string export declare function extractHost(uri: string): string
export declare function fetchMeta(): Promise<Meta> export declare function fetchMeta(): Promise<Meta>
@ -389,6 +425,13 @@ export interface Following {
followeeSharedInbox: string | null followeeSharedInbox: string | null
} }
export interface FollowRelay {
id: string
type: Activity
actor: string
object: string
}
export interface FollowRequest { export interface FollowRequest {
id: string id: string
createdAt: DateTimeWithTimeZone createdAt: DateTimeWithTimeZone
@ -448,10 +491,12 @@ export declare function getFullApAccount(username: string, host?: string | undef
export declare function getImageSizeFromUrl(url: string): Promise<ImageSize> export declare function getImageSizeFromUrl(url: string): Promise<ImageSize>
export declare function getInternalActor(actor: InternalActor): Promise<User> export declare function getInstanceActor(): Promise<User>
export declare function getNoteSummary(fileIds: Array<string>, text: string | undefined | null, cw: string | undefined | null, hasPoll: boolean): string export declare function getNoteSummary(fileIds: Array<string>, text: string | undefined | null, cw: string | undefined | null, hasPoll: boolean): string
export declare function getRelayActorId(): Promise<string>
export declare function getTimestamp(id: string): number export declare function getTimestamp(id: string): number
/** Prints the greeting message and the Firefish version to stdout. */ /** Prints the greeting message and the Firefish version to stdout. */
@ -529,8 +574,20 @@ export interface Instance {
faviconUrl: string | null faviconUrl: string | null
} }
export type InternalActor = 'instance'| export declare enum InternalEvent {
'relay'; Suspend = 0,
Silence = 1,
Moderator = 2,
Token = 3,
LocalUser = 4,
RemoteUser = 5,
WebhookCreated = 6,
WebhookUpdated = 7,
WebhookDeleted = 8,
AntennaCreated = 9,
AntennaUpdated = 10,
AntennaDeleted = 11
}
/** /**
* Checks if a server is allowlisted. * Checks if a server is allowlisted.
@ -832,6 +889,15 @@ export interface NoteEdit {
emojis: Array<string> emojis: Array<string>
} }
export declare enum NoteEvent {
Delete = 0,
React = 1,
Unreact = 2,
Reply = 3,
Update = 4,
Vote = 5
}
export interface NoteFavorite { export interface NoteFavorite {
id: string id: string
createdAt: DateTimeWithTimeZone createdAt: DateTimeWithTimeZone
@ -1113,10 +1179,20 @@ export declare function publishToDriveFolderStream(userId: string, kind: DriveFo
export declare function publishToGroupChatStream(groupId: string, kind: ChatEvent, object: any): Promise<void> export declare function publishToGroupChatStream(groupId: string, kind: ChatEvent, object: any): Promise<void>
export declare function publishToInternalStream(kind: InternalEvent, object: any): Promise<void>
export declare function publishToMainStream(userId: string, kind: Event, object: any): Promise<void>
export declare function publishToModerationStream(moderatorId: string, report: AbuseUserReportLike): Promise<void> export declare function publishToModerationStream(moderatorId: string, report: AbuseUserReportLike): Promise<void>
export declare function publishToNotesStream(note: Note): Promise<void> export declare function publishToNotesStream(note: Note): Promise<void>
export declare function publishToNoteStream(noteId: string, kind: NoteEvent, object: any): Promise<void>
export declare function publishToNoteUpdatesStream(note: Note): Promise<void>
export declare function publishToUserStream(userId: string, kind: UserEvent, object: any): Promise<void>
export interface PugArgs { export interface PugArgs {
img: string | null img: string | null
title: string title: string
@ -1189,6 +1265,8 @@ export type RelayStatus = 'accepted'|
/** Delete all entries in the [attestation_challenge] table created at more than 5 minutes ago */ /** Delete all entries in the [attestation_challenge] table created at more than 5 minutes ago */
export declare function removeOldAttestationChallenges(): Promise<void> export declare function removeOldAttestationChallenges(): Promise<void>
export declare function renderFollowRelay(relayId: string): Promise<FollowRelay>
export interface RenoteMuting { export interface RenoteMuting {
id: string id: string
createdAt: DateTimeWithTimeZone createdAt: DateTimeWithTimeZone
@ -1406,6 +1484,17 @@ export type UserEmojiModPerm = 'add'|
'mod'| 'mod'|
'unauthorized'; 'unauthorized';
export declare enum UserEvent {
Disconnect = 0,
FollowChannel = 1,
UnfollowChannel = 2,
UpdateProfile = 3,
Mute = 4,
Unmute = 5,
Follow = 6,
Unfollow = 7
}
export interface UserGroup { export interface UserGroup {
id: string id: string
createdAt: DateTimeWithTimeZone createdAt: DateTimeWithTimeZone

View file

@ -362,6 +362,7 @@ if (!nativeBinding) {
} }
module.exports.acctToString = nativeBinding.acctToString module.exports.acctToString = nativeBinding.acctToString
module.exports.Activity = nativeBinding.Activity
module.exports.AntennaSrc = nativeBinding.AntennaSrc module.exports.AntennaSrc = nativeBinding.AntennaSrc
module.exports.ChatEvent = nativeBinding.ChatEvent module.exports.ChatEvent = nativeBinding.ChatEvent
module.exports.ChatIndexEvent = nativeBinding.ChatIndexEvent module.exports.ChatIndexEvent = nativeBinding.ChatIndexEvent
@ -374,6 +375,7 @@ module.exports.decodeReaction = nativeBinding.decodeReaction
module.exports.DriveFileEvent = nativeBinding.DriveFileEvent module.exports.DriveFileEvent = nativeBinding.DriveFileEvent
module.exports.DriveFileUsageHint = nativeBinding.DriveFileUsageHint module.exports.DriveFileUsageHint = nativeBinding.DriveFileUsageHint
module.exports.DriveFolderEvent = nativeBinding.DriveFolderEvent module.exports.DriveFolderEvent = nativeBinding.DriveFolderEvent
module.exports.Event = nativeBinding.Event
module.exports.extractHost = nativeBinding.extractHost module.exports.extractHost = nativeBinding.extractHost
module.exports.fetchMeta = nativeBinding.fetchMeta module.exports.fetchMeta = nativeBinding.fetchMeta
module.exports.fetchNodeinfo = nativeBinding.fetchNodeinfo module.exports.fetchNodeinfo = nativeBinding.fetchNodeinfo
@ -384,14 +386,15 @@ module.exports.genId = nativeBinding.genId
module.exports.genIdAt = nativeBinding.genIdAt module.exports.genIdAt = nativeBinding.genIdAt
module.exports.getFullApAccount = nativeBinding.getFullApAccount module.exports.getFullApAccount = nativeBinding.getFullApAccount
module.exports.getImageSizeFromUrl = nativeBinding.getImageSizeFromUrl module.exports.getImageSizeFromUrl = nativeBinding.getImageSizeFromUrl
module.exports.getInternalActor = nativeBinding.getInternalActor module.exports.getInstanceActor = nativeBinding.getInstanceActor
module.exports.getNoteSummary = nativeBinding.getNoteSummary module.exports.getNoteSummary = nativeBinding.getNoteSummary
module.exports.getRelayActorId = nativeBinding.getRelayActorId
module.exports.getTimestamp = nativeBinding.getTimestamp module.exports.getTimestamp = nativeBinding.getTimestamp
module.exports.greet = nativeBinding.greet module.exports.greet = nativeBinding.greet
module.exports.hashPassword = nativeBinding.hashPassword module.exports.hashPassword = nativeBinding.hashPassword
module.exports.Inbound = nativeBinding.Inbound module.exports.Inbound = nativeBinding.Inbound
module.exports.initializeRustLogger = nativeBinding.initializeRustLogger module.exports.initializeRustLogger = nativeBinding.initializeRustLogger
module.exports.InternalActor = nativeBinding.InternalActor module.exports.InternalEvent = nativeBinding.InternalEvent
module.exports.isAllowedServer = nativeBinding.isAllowedServer module.exports.isAllowedServer = nativeBinding.isAllowedServer
module.exports.isBlockedServer = nativeBinding.isBlockedServer module.exports.isBlockedServer = nativeBinding.isBlockedServer
module.exports.isOldPasswordAlgorithm = nativeBinding.isOldPasswordAlgorithm module.exports.isOldPasswordAlgorithm = nativeBinding.isOldPasswordAlgorithm
@ -408,6 +411,7 @@ module.exports.metaToPugArgs = nativeBinding.metaToPugArgs
module.exports.MutedNoteReason = nativeBinding.MutedNoteReason module.exports.MutedNoteReason = nativeBinding.MutedNoteReason
module.exports.nodeinfo_2_0 = nativeBinding.nodeinfo_2_0 module.exports.nodeinfo_2_0 = nativeBinding.nodeinfo_2_0
module.exports.nodeinfo_2_1 = nativeBinding.nodeinfo_2_1 module.exports.nodeinfo_2_1 = nativeBinding.nodeinfo_2_1
module.exports.NoteEvent = nativeBinding.NoteEvent
module.exports.NoteVisibility = nativeBinding.NoteVisibility module.exports.NoteVisibility = nativeBinding.NoteVisibility
module.exports.NotificationType = nativeBinding.NotificationType module.exports.NotificationType = nativeBinding.NotificationType
module.exports.nyaify = nativeBinding.nyaify module.exports.nyaify = nativeBinding.nyaify
@ -422,12 +426,18 @@ module.exports.publishToChatStream = nativeBinding.publishToChatStream
module.exports.publishToDriveFileStream = nativeBinding.publishToDriveFileStream module.exports.publishToDriveFileStream = nativeBinding.publishToDriveFileStream
module.exports.publishToDriveFolderStream = nativeBinding.publishToDriveFolderStream module.exports.publishToDriveFolderStream = nativeBinding.publishToDriveFolderStream
module.exports.publishToGroupChatStream = nativeBinding.publishToGroupChatStream module.exports.publishToGroupChatStream = nativeBinding.publishToGroupChatStream
module.exports.publishToInternalStream = nativeBinding.publishToInternalStream
module.exports.publishToMainStream = nativeBinding.publishToMainStream
module.exports.publishToModerationStream = nativeBinding.publishToModerationStream module.exports.publishToModerationStream = nativeBinding.publishToModerationStream
module.exports.publishToNotesStream = nativeBinding.publishToNotesStream module.exports.publishToNotesStream = nativeBinding.publishToNotesStream
module.exports.publishToNoteStream = nativeBinding.publishToNoteStream
module.exports.publishToNoteUpdatesStream = nativeBinding.publishToNoteUpdatesStream
module.exports.publishToUserStream = nativeBinding.publishToUserStream
module.exports.PushNotificationKind = nativeBinding.PushNotificationKind module.exports.PushNotificationKind = nativeBinding.PushNotificationKind
module.exports.PushSubscriptionType = nativeBinding.PushSubscriptionType module.exports.PushSubscriptionType = nativeBinding.PushSubscriptionType
module.exports.RelayStatus = nativeBinding.RelayStatus module.exports.RelayStatus = nativeBinding.RelayStatus
module.exports.removeOldAttestationChallenges = nativeBinding.removeOldAttestationChallenges module.exports.removeOldAttestationChallenges = nativeBinding.removeOldAttestationChallenges
module.exports.renderFollowRelay = nativeBinding.renderFollowRelay
module.exports.safeForSql = nativeBinding.safeForSql module.exports.safeForSql = nativeBinding.safeForSql
module.exports.sendPushNotification = nativeBinding.sendPushNotification module.exports.sendPushNotification = nativeBinding.sendPushNotification
module.exports.shouldNyaify = nativeBinding.shouldNyaify module.exports.shouldNyaify = nativeBinding.shouldNyaify
@ -445,6 +455,7 @@ module.exports.updateAntennasOnNewNote = nativeBinding.updateAntennasOnNewNote
module.exports.updateMetaCache = nativeBinding.updateMetaCache module.exports.updateMetaCache = nativeBinding.updateMetaCache
module.exports.updateNodeinfoCache = nativeBinding.updateNodeinfoCache module.exports.updateNodeinfoCache = nativeBinding.updateNodeinfoCache
module.exports.UserEmojiModPerm = nativeBinding.UserEmojiModPerm module.exports.UserEmojiModPerm = nativeBinding.UserEmojiModPerm
module.exports.UserEvent = nativeBinding.UserEvent
module.exports.UserProfileFfvisibility = nativeBinding.UserProfileFfvisibility module.exports.UserProfileFfvisibility = nativeBinding.UserProfileFfvisibility
module.exports.UserProfileMutingNotificationTypes = nativeBinding.UserProfileMutingNotificationTypes module.exports.UserProfileMutingNotificationTypes = nativeBinding.UserProfileMutingNotificationTypes
module.exports.verifyPassword = nativeBinding.verifyPassword module.exports.verifyPassword = nativeBinding.verifyPassword

View file

@ -200,7 +200,7 @@ pub struct Config {
pub proxy_smtp: Option<String>, pub proxy_smtp: Option<String>,
pub proxy_bypass_hosts: Option<Vec<String>>, pub proxy_bypass_hosts: Option<Vec<String>>,
pub allowed_private_networks: Option<Vec<String>>, pub allowed_private_networks: Option<Vec<String>>,
pub max_file_size: Option<i64>, pub max_file_size: i64,
pub access_log: Option<String>, pub access_log: Option<String>,
pub cluster_limits: WorkerConfig, pub cluster_limits: WorkerConfig,
pub cuid: Option<IdConfig>, pub cuid: Option<IdConfig>,
@ -309,7 +309,7 @@ pub fn load_config() -> Config {
proxy_smtp: server_config.proxy_smtp, proxy_smtp: server_config.proxy_smtp,
proxy_bypass_hosts: server_config.proxy_bypass_hosts, proxy_bypass_hosts: server_config.proxy_bypass_hosts,
allowed_private_networks: server_config.allowed_private_networks, allowed_private_networks: server_config.allowed_private_networks,
max_file_size: server_config.max_file_size, max_file_size: server_config.max_file_size.unwrap_or(262144000),
access_log: server_config.access_log, access_log: server_config.access_log,
cluster_limits, cluster_limits,
cuid: server_config.cuid, cuid: server_config.cuid,

View file

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

View file

@ -0,0 +1,9 @@
pub mod relay;
pub trait ActivityPubObject {}
#[derive(serde::Serialize)]
#[macros::export(string_enum)]
pub enum Activity {
Follow,
}

View file

@ -0,0 +1,28 @@
use super::*;
use crate::{config::CONFIG, federation::internal_actor};
use serde::Serialize;
#[derive(Serialize)]
#[macros::export(object)]
pub struct FollowRelay {
pub id: String,
pub r#type: Activity,
pub actor: String,
pub object: String,
}
impl ActivityPubObject for FollowRelay {}
#[macros::export(js_name = "renderFollowRelay")]
pub async fn follow(relay_id: &str) -> Result<FollowRelay, internal_actor::relay::Error> {
Ok(FollowRelay {
id: format!("{}/activities/follow-relay/{}", CONFIG.url, relay_id),
r#type: Activity::Follow,
actor: format!(
"{}/users/{}",
CONFIG.url,
internal_actor::relay::get_id().await?
),
object: "https://www.w3.org/ns/activitystreams#Public".to_owned(),
})
}

View file

@ -1,87 +0,0 @@
//! In-memory internal actor cache handler
// TODO: refactoring
use super::*;
use crate::{database::db_conn, model::entity::user};
use sea_orm::prelude::*;
use std::sync::Mutex;
#[macros::errors]
pub enum Error {
#[error(transparent)]
#[doc = "database error"]
Db(#[from] DbErr),
#[error("{} does not exist", Acct::from(.0.to_owned()))]
#[doc = "internal actor does not exist"]
InternalActorNotFound(InternalActor),
}
static INSTANCE_ACTOR: Mutex<Option<user::Model>> = Mutex::new(None);
static RELAY_ACTOR: Mutex<Option<user::Model>> = Mutex::new(None);
fn set_instance_actor(value: &user::Model) {
let _ = INSTANCE_ACTOR
.lock()
.map(|mut cache| *cache = Some(value.to_owned()));
}
fn set_relay_actor(value: &user::Model) {
let _ = RELAY_ACTOR
.lock()
.map(|mut cache| *cache = Some(value.to_owned()));
}
async fn cache_instance_actor() -> Result<user::Model, Error> {
let actor = user::Entity::find()
.filter(user::Column::Username.eq(INSTANCE_ACTOR_USERNAME))
.filter(user::Column::Host.is_null())
.one(db_conn().await?)
.await?;
if let Some(actor) = actor {
set_instance_actor(&actor);
Ok(actor)
} else {
Err(Error::InternalActorNotFound(InternalActor::Instance))
}
}
async fn cache_relay_actor() -> Result<user::Model, Error> {
let actor = user::Entity::find()
.filter(user::Column::Username.eq(RELAY_ACTOR_USERNAME))
.filter(user::Column::Host.is_null())
.one(db_conn().await?)
.await?;
if let Some(actor) = actor {
set_relay_actor(&actor);
Ok(actor)
} else {
Err(Error::InternalActorNotFound(InternalActor::Relay))
}
}
// for napi export
// https://github.com/napi-rs/napi-rs/issues/2060
type User = user::Model;
#[macros::export(js_name = "getInternalActor")]
pub async fn get(actor: InternalActor) -> Result<User, Error> {
match actor {
InternalActor::Instance => {
if let Some(cache) = INSTANCE_ACTOR.lock().ok().and_then(|cache| cache.clone()) {
tracing::debug!("Using cached instance.actor");
return Ok(cache);
}
tracing::debug!("Caching instance.actor");
cache_instance_actor().await
}
InternalActor::Relay => {
if let Some(cache) = RELAY_ACTOR.lock().ok().and_then(|cache| cache.clone()) {
tracing::debug!("Using cached relay.actor");
return Ok(cache);
}
tracing::debug!("Caching relay.actor");
cache_relay_actor().await
}
}
}

View file

@ -0,0 +1,50 @@
//! In-memory instance actor cache
use crate::{database::db_conn, model::entity::user};
use sea_orm::prelude::*;
use tokio::sync::OnceCell;
// for napi export
// https://github.com/napi-rs/napi-rs/issues/2060
type User = user::Model;
pub const USERNAME: &str = "instance.actor";
static INSTANCE_ACTOR: OnceCell<User> = OnceCell::const_new();
#[macros::errors]
pub enum Error {
#[error("@instance.actor not found")]
InstanceActorNotFound,
#[error(transparent)]
#[doc = "database error"]
Db(#[from] DbErr),
}
async fn set_cache() -> Result<&'static User, Error> {
let instance_actor = INSTANCE_ACTOR
.get_or_try_init(|| async {
tracing::debug!("caching @instance.actor");
let found_model = user::Entity::find()
.filter(user::Column::Username.eq(USERNAME))
.filter(user::Column::Host.is_null())
.one(db_conn().await?)
.await?;
found_model.ok_or(Error::InstanceActorNotFound)
})
.await?;
Ok(instance_actor)
}
pub async fn get() -> Result<&'static User, Error> {
match INSTANCE_ACTOR.get() {
Some(model) => Ok(model),
None => set_cache().await,
}
}
#[macros::ts_export(js_name = "getInstanceActor")]
pub async fn get_js() -> Result<User, Error> {
Ok(get().await?.to_owned())
}

View file

@ -1,34 +1,4 @@
mod cache; pub mod instance;
pub mod relay;
pub use cache::get;
use super::acct::Acct;
#[derive(Debug)]
#[macros::derive_clone_and_export(string_enum = "lowercase")]
pub enum InternalActor {
Instance,
Relay,
}
const INSTANCE_ACTOR_USERNAME: &str = "instance.actor";
const RELAY_ACTOR_USERNAME: &str = "relay.actor";
// TODO: When `std::mem::variant_count` is stabilized, use
// it to count system actors instead of hard coding the magic number
pub const INTERNAL_ACTORS: u64 = 2; pub const INTERNAL_ACTORS: u64 = 2;
impl From<InternalActor> for Acct {
fn from(actor: InternalActor) -> Self {
match actor {
InternalActor::Instance => Acct {
username: INSTANCE_ACTOR_USERNAME.to_owned(),
host: None,
},
InternalActor::Relay => Acct {
username: RELAY_ACTOR_USERNAME.to_owned(),
host: None,
},
}
}
}

View file

@ -0,0 +1,45 @@
//! In-memory relay actor id cache
use crate::{database::db_conn, model::entity::user};
use sea_orm::{prelude::*, QuerySelect, SelectColumns};
use tokio::sync::OnceCell;
pub const USERNAME: &str = "relay.actor";
static RELAY_ACTOR_ID: OnceCell<String> = OnceCell::const_new();
#[macros::errors]
pub enum Error {
#[error("@relay.actor not found")]
RelayActorNotFound,
#[error(transparent)]
#[doc = "database error"]
Db(#[from] DbErr),
}
async fn set_id_cache() -> Result<&'static str, Error> {
let id = RELAY_ACTOR_ID
.get_or_try_init(|| async {
tracing::debug!("caching @relay.actor");
let found_id = user::Entity::find()
.select_only()
.select_column(user::Column::Id)
.filter(user::Column::Username.eq(USERNAME))
.filter(user::Column::Host.is_null())
.into_tuple::<String>()
.one(db_conn().await?)
.await?;
found_id.ok_or(Error::RelayActorNotFound)
})
.await?;
Ok(id)
}
#[macros::export(js_name = "getRelayActorId")]
pub async fn get_id() -> Result<&'static str, Error> {
match RELAY_ACTOR_ID.get() {
Some(id) => Ok(id),
None => set_id_cache().await,
}
}

View file

@ -1,5 +1,6 @@
//! Services used to federate with other servers //! Services used to federate with other servers
pub mod acct; pub mod acct;
pub mod activitypub;
pub mod internal_actor; pub mod internal_actor;
pub mod nodeinfo; pub mod nodeinfo;

View file

@ -5,8 +5,13 @@ pub mod chat_index;
pub mod custom_emoji; pub mod custom_emoji;
pub mod drive; pub mod drive;
pub mod group_chat; pub mod group_chat;
pub mod internal;
pub mod main;
pub mod moderation; pub mod moderation;
pub mod note;
pub mod note_edit;
pub mod notes; pub mod notes;
pub mod user;
use crate::{ use crate::{
config::CONFIG, config::CONFIG,
@ -30,9 +35,7 @@ pub enum Stream {
note_id: String, note_id: String,
}, },
Notes, Notes,
UserList { NoteEdit,
list_id: String,
},
Main { Main {
user_id: String, user_id: String,
}, },
@ -86,8 +89,8 @@ pub async fn publish_to_stream(
Stream::User { user_id } => format!("user:{user_id}"), Stream::User { user_id } => format!("user:{user_id}"),
Stream::Channel { channel_id } => format!("channelStream:{channel_id}"), Stream::Channel { channel_id } => format!("channelStream:{channel_id}"),
Stream::Note { note_id } => format!("noteStream:{note_id}"), Stream::Note { note_id } => format!("noteStream:{note_id}"),
Stream::NoteEdit => "noteUpdatesStream".to_owned(),
Stream::Notes => "notesStream".to_owned(), Stream::Notes => "notesStream".to_owned(),
Stream::UserList { list_id } => format!("userListStream:{list_id}"),
Stream::Main { user_id } => format!("mainStream:{user_id}"), Stream::Main { user_id } => format!("mainStream:{user_id}"),
Stream::Drive { user_id } => format!("driveStream:{user_id}"), Stream::Drive { user_id } => format!("driveStream:{user_id}"),
Stream::Antenna { antenna_id } => format!("antennaStream:{antenna_id}"), Stream::Antenna { antenna_id } => format!("antennaStream:{antenna_id}"),

View file

@ -0,0 +1,45 @@
use crate::service::stream::{publish_to_stream, Error, Stream};
#[macros::export]
pub enum InternalEvent {
Suspend,
Silence,
Moderator,
Token,
LocalUser,
RemoteUser,
WebhookCreated,
WebhookUpdated,
WebhookDeleted,
AntennaCreated,
AntennaUpdated,
AntennaDeleted,
}
// We want to merge `kind` and `object` into a single enum
// https://github.com/napi-rs/napi-rs/issues/2036
#[macros::export(js_name = "publishToInternalStream")]
pub async fn publish(kind: InternalEvent, object: &serde_json::Value) -> Result<(), Error> {
let kind = match kind {
InternalEvent::Suspend => "userChangeSuspendedState",
InternalEvent::Silence => "userChangeSilencedState",
InternalEvent::Moderator => "userChangeModeratorState",
InternalEvent::Token => "userTokenRegenerated",
InternalEvent::LocalUser => "localUserUpdated",
InternalEvent::RemoteUser => "remoteUserUpdated",
InternalEvent::WebhookCreated => "webhookCreated",
InternalEvent::WebhookUpdated => "webhookUpdated",
InternalEvent::WebhookDeleted => "webhookDeleted",
InternalEvent::AntennaCreated => "antennaCreated",
InternalEvent::AntennaUpdated => "antennaUpdated",
InternalEvent::AntennaDeleted => "antennaDeleted",
};
publish_to_stream(
&Stream::Internal,
Some(kind),
Some(serde_json::to_string(&object)?),
)
.await
}

View file

@ -0,0 +1,87 @@
use crate::service::stream::{publish_to_stream, Error, Stream};
#[macros::export]
pub enum Event {
Notification,
NewNotification,
Mention,
NewMention,
Chat,
NewChat,
NewDm,
Reply,
Renote,
Follow,
Followed,
Unfollow,
NewFollowRequest,
Page,
ReadAllNotifications,
ReadAllMentions,
ReadNotifications,
ReadAllDms,
ReadAllChats,
ReadAntenna,
ReadAllAntennaPosts,
NewAntennaPost,
ReadAllAnnouncements,
ReadAllChannelPosts,
NewChannelPost,
DriveFile,
UrlUploadFinished,
Me,
RegenerateMyToken,
Signin,
Registry,
}
// We want to merge `kind` and `object` into a single enum
// https://github.com/napi-rs/napi-rs/issues/2036
#[macros::export(js_name = "publishToMainStream")]
pub async fn publish(
user_id: String,
kind: Event,
object: &serde_json::Value,
) -> Result<(), Error> {
let kind = match kind {
Event::Notification => "notification",
Event::Mention => "mention",
Event::Reply => "reply",
Event::Renote => "renote",
Event::Follow => "follow",
Event::Followed => "followed",
Event::Unfollow => "unfollow",
Event::Me => "meUpdated",
Event::Page => "pageEvent",
Event::UrlUploadFinished => "urlUploadFinished",
Event::ReadAllNotifications => "readAllNotifications",
Event::ReadNotifications => "readNotifications",
Event::NewNotification => "unreadNotification",
Event::NewMention => "unreadMention",
Event::ReadAllMentions => "readAllUnreadMentions",
Event::ReadAllDms => "readAllUnreadSpecifiedNotes",
Event::NewDm => "unreadSpecifiedNote",
Event::ReadAllChats => "readAllMessagingMessages",
Event::Chat => "messagingMessage",
Event::NewChat => "unreadMessagingMessage",
Event::ReadAllAntennaPosts => "readAllAntennas",
Event::NewAntennaPost => "unreadAntenna",
Event::ReadAllAnnouncements => "readAllAnnouncements",
Event::ReadAllChannelPosts => "readAllChannels",
Event::NewChannelPost => "unreadChannel",
Event::RegenerateMyToken => "myTokenRegenerated",
Event::Signin => "signin",
Event::Registry => "registryUpdated",
Event::DriveFile => "driveFileCreated",
Event::ReadAntenna => "readAntenna",
Event::NewFollowRequest => "receiveFollowRequest",
};
publish_to_stream(
&Stream::Main { user_id },
Some(kind),
Some(serde_json::to_string(&object)?),
)
.await
}

View file

@ -0,0 +1,43 @@
use crate::service::stream::{publish_to_stream, Error, Stream};
use serde_json::json;
#[macros::export]
pub enum NoteEvent {
Delete,
React,
Unreact,
Reply,
Update,
Vote,
}
// We want to merge `kind` and `object` into a single enum
// https://github.com/napi-rs/napi-rs/issues/2036
#[macros::export(js_name = "publishToNoteStream")]
pub async fn publish(
note_id: String,
kind: NoteEvent,
object: &serde_json::Value,
) -> Result<(), Error> {
let kind = match kind {
NoteEvent::Delete => "deleted",
NoteEvent::React => "reacted",
NoteEvent::Unreact => "unreacted",
NoteEvent::Reply => "replied",
NoteEvent::Update => "updated",
NoteEvent::Vote => "pollVoted",
};
let value = json!({
"id": note_id.clone(),
"body": object,
});
publish_to_stream(
&Stream::Note { note_id },
Some(kind),
Some(serde_json::to_string(&value)?),
)
.await
}

View file

@ -0,0 +1,18 @@
use crate::{
model::entity::note,
service::stream::{publish_to_stream, Error, Stream},
};
// for napi export
// https://github.com/napi-rs/napi-rs/issues/2060
type Note = note::Model;
#[macros::export(js_name = "publishToNoteUpdatesStream")]
pub async fn publish(note: &Note) -> Result<(), Error> {
publish_to_stream(
&Stream::NoteEdit,
Some("updated"),
Some(serde_json::to_string(note)?),
)
.await
}

View file

@ -0,0 +1,41 @@
use crate::service::stream::{publish_to_stream, Error, Stream};
#[macros::export]
pub enum UserEvent {
Disconnect,
FollowChannel,
UnfollowChannel,
UpdateProfile,
Mute,
Unmute,
Follow,
Unfollow,
}
// We want to merge `kind` and `object` into a single enum
// https://github.com/napi-rs/napi-rs/issues/2036
#[macros::export(js_name = "publishToUserStream")]
pub async fn publish(
user_id: String,
kind: UserEvent,
object: &serde_json::Value,
) -> Result<(), Error> {
let kind = match kind {
UserEvent::Disconnect => "terminate",
UserEvent::FollowChannel => "followChannel",
UserEvent::UnfollowChannel => "unfollowChannel",
UserEvent::UpdateProfile => "updateUserProfile",
UserEvent::Mute => "mute",
UserEvent::Unmute => "unmute",
UserEvent::Follow => "follow",
UserEvent::Unfollow => "unfollow",
};
publish_to_stream(
&Stream::User { user_id },
Some(kind),
Some(serde_json::to_string(&object)?),
)
.await
}

View file

@ -33,7 +33,7 @@
"archiver": "7.0.1", "archiver": "7.0.1",
"async-lock": "1.4.1", "async-lock": "1.4.1",
"async-mutex": "0.5.0", "async-mutex": "0.5.0",
"aws-sdk": "2.1662.0", "aws-sdk": "2.1664.0",
"axios": "1.7.2", "axios": "1.7.2",
"backend-rs": "workspace:*", "backend-rs": "workspace:*",
"blurhash": "2.0.5", "blurhash": "2.0.5",

View file

@ -18,7 +18,6 @@ export const DB_MAX_NOTE_TEXT_LENGTH = 100000;
*/ */
export const DB_MAX_IMAGE_COMMENT_LENGTH = 8192; export const DB_MAX_IMAGE_COMMENT_LENGTH = 8192;
export const MAX_NOTE_TEXT_LENGTH = Math.min( export const MAX_NOTE_TEXT_LENGTH = Math.min(
config.maxNoteLength ?? 3000, config.maxNoteLength ?? 3000,
DB_MAX_NOTE_TEXT_LENGTH, DB_MAX_NOTE_TEXT_LENGTH,

View file

@ -13,8 +13,8 @@ export default async function () {
ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length || 50)); ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length || 50));
}); });
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (!meta.enableServerMachineStats) return; if (!instanceMeta.enableServerMachineStats) return;
async function tick() { async function tick() {
const stats = { const stats = {

View file

@ -1,9 +1,29 @@
import type { MigrationInterface, QueryRunner } from "typeorm"; import type { MigrationInterface, QueryRunner } from "typeorm";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
import { genRsaKeyPair } from "@/misc/gen-key-pair.js";
import { generateUserToken, genIdAt, hashPassword } from "backend-rs"; import { generateUserToken, genIdAt, hashPassword } from "backend-rs";
import * as crypto from "node:crypto";
import * as util from "node:util";
const generateKeyPair = util.promisify(crypto.generateKeyPair);
async function genRsaKeyPair(modulusLength = 2048) {
return await generateKeyPair("rsa", {
modulusLength,
publicKeyEncoding: {
type: "spki",
format: "pem",
},
privateKeyEncoding: {
type: "pkcs8",
format: "pem",
cipher: undefined,
passphrase: undefined,
},
});
}
async function createSystemUser(username: string, queryRunner: QueryRunner) { async function createSystemUser(username: string, queryRunner: QueryRunner) {
const password = uuid(); const password = uuid();

View file

@ -20,7 +20,7 @@ export async function downloadUrl(url: string, path: string): Promise<void> {
const timeout = 30 * 1000; const timeout = 30 * 1000;
const operationTimeout = 60 * 1000; const operationTimeout = 60 * 1000;
const maxSize = config.maxFileSize || 262144000; const maxSize = config.maxFileSize;
const req = got const req = got
.stream(url, { .stream(url, {

View file

@ -3,9 +3,9 @@ import type { ILocalUser } from "@/models/entities/user.js";
import { Users } from "@/models/index.js"; import { Users } from "@/models/index.js";
export async function fetchProxyAccount(): Promise<ILocalUser | null> { export async function fetchProxyAccount(): Promise<ILocalUser | null> {
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.proxyAccountId == null) return null; if (instanceMeta.proxyAccountId == null) return null;
return (await Users.findOneByOrFail({ return (await Users.findOneByOrFail({
id: meta.proxyAccountId, id: instanceMeta.proxyAccountId,
})) as ILocalUser; })) as ILocalUser;
} }

View file

@ -1,42 +0,0 @@
import * as crypto from "node:crypto";
import * as util from "node:util";
const generateKeyPair = util.promisify(crypto.generateKeyPair);
export async function genRsaKeyPair(modulusLength = 2048) {
return await generateKeyPair("rsa", {
modulusLength,
publicKeyEncoding: {
type: "spki",
format: "pem",
},
privateKeyEncoding: {
type: "pkcs8",
format: "pem",
cipher: undefined,
passphrase: undefined,
},
});
}
export async function genEcKeyPair(
namedCurve:
| "prime256v1"
| "secp384r1"
| "secp521r1"
| "curve25519" = "prime256v1",
) {
return await generateKeyPair("ec", {
namedCurve,
publicKeyEncoding: {
type: "spki",
format: "pem",
},
privateKeyEncoding: {
type: "pkcs8",
format: "pem",
cipher: undefined,
passphrase: undefined,
},
});
}

View file

@ -1,25 +0,0 @@
import { noteVisibilities } from "@/types.js";
export type Post = {
text: string | undefined;
cw: string | null;
localOnly: boolean;
createdAt: Date;
visibility: string;
};
export function parse(acct: any): Post {
return {
text: acct.text || undefined,
cw: acct.cw,
localOnly: acct.localOnly,
createdAt: new Date(acct.createdAt),
visibility: noteVisibilities.includes(acct.visibility)
? acct.visibility
: "specified",
};
}
export function toJson(acct: Post): string {
return { text: acct.text, cw: acct.cw, localOnly: acct.localOnly }.toString();
}

View file

@ -231,13 +231,13 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
if (url.startsWith(`${config.url}/identicon`)) return url; if (url.startsWith(`${config.url}/identicon`)) return url;
if (url.startsWith(`${config.url}/avatar`)) return url; if (url.startsWith(`${config.url}/avatar`)) return url;
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
const baseUrl = meta const baseUrl = instanceMeta
? meta.objectStorageBaseUrl ?? ? instanceMeta.objectStorageBaseUrl ??
`${meta.objectStorageUseSsl ? "https" : "http"}://${ `${instanceMeta.objectStorageUseSsl ? "https" : "http"}://${
meta.objectStorageEndpoint instanceMeta.objectStorageEndpoint
}${meta.objectStoragePort ? `:${meta.objectStoragePort}` : ""}/${ }${instanceMeta.objectStoragePort ? `:${instanceMeta.objectStoragePort}` : ""}/${
meta.objectStorageBucket instanceMeta.objectStorageBucket
}` }`
: null; : null;
if (baseUrl !== null && url.startsWith(baseUrl)) return url; if (baseUrl !== null && url.startsWith(baseUrl)) return url;

View file

@ -155,13 +155,13 @@ webhookDeliverQueue
webhookLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`), webhookLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`),
); );
export function deliver(user: ThinUser, content: unknown, to: string | null) { export function deliver(userId: string, content: unknown, to: string | null) {
if (content == null) return null; if (content == null) return null;
if (to == null) return null; if (to == null) return null;
const data = { const data = {
user: { user: {
id: user.id, id: userId,
}, },
content, content,
to, to,

View file

@ -1,4 +1,3 @@
import * as Post from "@/misc/post.js";
import create from "@/services/note/create.js"; import create from "@/services/note/create.js";
import { NoteFiles, Users } from "@/models/index.js"; import { NoteFiles, Users } from "@/models/index.js";
import type { DbUserImportMastoPostJobData } from "@/queue/types.js"; import type { DbUserImportMastoPostJobData } from "@/queue/types.js";
@ -10,6 +9,7 @@ import { createImportCkPostJob } from "@/queue/index.js";
import { Notes, NoteEdits } from "@/models/index.js"; import { Notes, NoteEdits } from "@/models/index.js";
import type { Note } from "@/models/entities/note.js"; import type { Note } from "@/models/entities/note.js";
import { genId } from "backend-rs"; import { genId } from "backend-rs";
import { noteVisibilities } from "@/types.js";
const logger = queueLogger.createSubLogger("import-firefish-post"); const logger = queueLogger.createSubLogger("import-firefish-post");
@ -52,10 +52,15 @@ export async function importCkPost(
logger.info(`Skipped adding file to drive: ${url}`); logger.info(`Skipped adding file to drive: ${url}`);
} }
} }
const { text, cw, localOnly, createdAt, visibility } = Post.parse(post);
const createdAt = new Date(post.createdAt);
const visibility = noteVisibilities.includes(post.visibility)
? post.visibility
: "specified";
let note = await Notes.findOneBy({ let note = await Notes.findOneBy({
createdAt: createdAt, createdAt,
text: text, text: post.text || undefined,
userId: user.id, userId: user.id,
}); });
@ -95,16 +100,16 @@ export async function importCkPost(
note = await create( note = await create(
user, user,
{ {
createdAt: createdAt, createdAt,
scheduledAt: undefined, scheduledAt: undefined,
files: files.length === 0 ? undefined : files, files: files.length === 0 ? undefined : files,
poll: undefined, poll: undefined,
text: text || undefined, text: post.text || undefined,
reply: post.replyId ? job.data.parent : null, reply: post.replyId ? job.data.parent : null,
renote: post.renoteId ? job.data.parent : null, renote: post.renoteId ? job.data.parent : null,
cw: cw, cw: post.cw,
localOnly, localOnly: post.localOnly,
visibility: visibility, visibility,
visibleUsers: [], visibleUsers: [],
channel: null, channel: null,
apMentions: new Array(0), apMentions: new Array(0),

View file

@ -2,7 +2,7 @@ import type Bull from "bull";
import { In } from "typeorm"; import { In } from "typeorm";
import { Mutings } from "@/models/index.js"; import { Mutings } from "@/models/index.js";
import { queueLogger } from "../../logger.js"; import { queueLogger } from "../../logger.js";
import { publishUserEvent } from "@/services/stream.js"; import { publishToUserStream, UserEvent } from "backend-rs";
const logger = queueLogger.createSubLogger("check-expired-mutings"); const logger = queueLogger.createSubLogger("check-expired-mutings");
@ -23,9 +23,11 @@ export async function checkExpiredMutings(
id: In(expired.map((m) => m.id)), id: In(expired.map((m) => m.id)),
}); });
for (const m of expired) { await Promise.all(
publishUserEvent(m.muterId, "unmute", m.mutee!); expired.map((m) =>
} publishToUserStream(m.muterId, UserEvent.Unmute, m.mutee),
),
);
} }
logger.info("All expired mutings checked."); logger.info("All expired mutings checked.");

View file

@ -15,8 +15,8 @@ import type { UserPublickey } from "@/models/entities/user-publickey.js";
import { verify } from "node:crypto"; import { verify } from "node:crypto";
export async function hasSignature(req: IncomingMessage): Promise<string> { export async function hasSignature(req: IncomingMessage): Promise<string> {
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
const required = meta.secureMode || meta.privateMode; const required = instanceMeta.secureMode || instanceMeta.privateMode;
try { try {
httpSignature.parseRequest(req, { headers: [] }); httpSignature.parseRequest(req, { headers: [] });
@ -30,8 +30,8 @@ export async function hasSignature(req: IncomingMessage): Promise<string> {
} }
export async function checkFetch(req: IncomingMessage): Promise<number> { export async function checkFetch(req: IncomingMessage): Promise<number> {
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
if (req.headers.host !== config.host) return 400; if (req.headers.host !== config.host) return 400;
let signature; let signature;

View file

@ -148,7 +148,7 @@ export default class DeliverManager {
// skip instances as indicated // skip instances as indicated
if (instancesToSkip.includes(valid.host)) continue; if (instancesToSkip.includes(valid.host)) continue;
deliver(this.actor, this.activity, valid.inbox); deliver(this.actor.id, this.activity, valid.inbox);
} }
} }
} }

View file

@ -36,14 +36,14 @@ export async function createImage(
apLogger.info(`Creating an image: ${image.url}`); apLogger.info(`Creating an image: ${image.url}`);
const instance = await fetchMeta(); const instanceMeta = await fetchMeta();
let file = await uploadFromUrl({ let file = await uploadFromUrl({
url: image.url, url: image.url,
user: actor, user: actor,
uri: image.url, uri: image.url,
sensitive: image.sensitive, sensitive: image.sensitive,
isLink: !instance.cacheRemoteFiles, isLink: !instanceMeta.cacheRemoteFiles,
comment: truncate(image.name, config.maxCaptionLength), comment: truncate(image.name, config.maxCaptionLength),
usageHint: usage, usageHint: usage,
}); });

View file

@ -15,11 +15,13 @@ import { apLogger } from "../logger.js";
import type { DriveFile } from "@/models/entities/drive-file.js"; import type { DriveFile } from "@/models/entities/drive-file.js";
import { import {
type ImageSize, type ImageSize,
NoteEvent,
extractHost, extractHost,
genId, genId,
getImageSizeFromUrl, getImageSizeFromUrl,
isBlockedServer, isBlockedServer,
isSameOrigin, isSameOrigin,
publishToNoteStream,
toPuny, toPuny,
} from "backend-rs"; } from "backend-rs";
import { import {
@ -47,7 +49,6 @@ import { parseAudience } from "../audience.js";
import { extractApMentions } from "./mention.js"; import { extractApMentions } from "./mention.js";
import DbResolver from "../db-resolver.js"; import DbResolver from "../db-resolver.js";
import { StatusError } from "@/misc/fetch.js"; import { StatusError } from "@/misc/fetch.js";
import { publishNoteStream } from "@/services/stream.js";
import { extractHashtags } from "@/misc/extract-hashtags.js"; import { extractHashtags } from "@/misc/extract-hashtags.js";
import { UserProfiles } from "@/models/index.js"; import { UserProfiles } from "@/models/index.js";
import { In } from "typeorm"; import { In } from "typeorm";
@ -795,7 +796,7 @@ export async function updateNote(value: string | IObject, resolver?: Resolver) {
if (publishing) { if (publishing) {
// Publish update event for the updated note details // Publish update event for the updated note details
publishNoteStream(note.id, "updated", { publishToNoteStream(note.id, NoteEvent.Update, {
updatedAt: update.updatedAt, updatedAt: update.updatedAt,
}); });
} }

View file

@ -16,7 +16,14 @@ import type { IRemoteUser, CacheableUser } from "@/models/entities/user.js";
import { User } from "@/models/entities/user.js"; import { User } from "@/models/entities/user.js";
import type { Emoji } from "@/models/entities/emoji.js"; import type { Emoji } from "@/models/entities/emoji.js";
import { UserNotePining } from "@/models/entities/user-note-pining.js"; import { UserNotePining } from "@/models/entities/user-note-pining.js";
import { genId, genIdAt, isSameOrigin, toPuny } from "backend-rs"; import {
genId,
genIdAt,
InternalEvent,
isSameOrigin,
publishToInternalStream,
toPuny,
} from "backend-rs";
import { UserPublickey } from "@/models/entities/user-publickey.js"; import { UserPublickey } from "@/models/entities/user-publickey.js";
import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js"; import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
import { UserProfile } from "@/models/entities/user-profile.js"; import { UserProfile } from "@/models/entities/user-profile.js";
@ -26,7 +33,6 @@ import { normalizeForSearch } from "@/misc/normalize-for-search.js";
import { truncate } from "@/misc/truncate.js"; import { truncate } from "@/misc/truncate.js";
import { StatusError } from "@/misc/fetch.js"; import { StatusError } from "@/misc/fetch.js";
import { uriPersonCache } from "@/services/user-cache.js"; import { uriPersonCache } from "@/services/user-cache.js";
import { publishInternalEvent } from "@/services/stream.js";
import { db } from "@/db/postgre.js"; import { db } from "@/db/postgre.js";
import { apLogger } from "../logger.js"; import { apLogger } from "../logger.js";
import { htmlToMfm } from "../misc/html-to-mfm.js"; import { htmlToMfm } from "../misc/html-to-mfm.js";
@ -611,7 +617,7 @@ export async function updatePerson(
}, },
); );
publishInternalEvent("remoteUserUpdated", { id: user.id }); publishToInternalStream(InternalEvent.RemoteUser, { id: user.id });
// Hashtag Update // Hashtag Update
updateUsertags(user, tags); updateUsertags(user, tags);

View file

@ -1,14 +0,0 @@
import { config } from "@/config.js";
import type { Relay } from "@/models/entities/relay.js";
import type { ILocalUser } from "@/models/entities/user.js";
export function renderFollowRelay(relay: Relay, relayActor: ILocalUser) {
const follow = {
id: `${config.url}/activities/follow-relay/${relay.id}`,
type: "Follow",
actor: `${config.url}/users/${relayActor.id}`,
object: "https://www.w3.org/ns/activitystreams#Public",
};
return follow;
}

View file

@ -1,7 +1,6 @@
import { config } from "@/config.js"; import { config } from "@/config.js";
import type { User } from "@/models/entities/user.js";
export default (object: any, user: { id: User["id"] }) => { export const renderUndo = (object: any, userId: string) => {
if (object == null) return null; if (object == null) return null;
const id = const id =
typeof object.id === "string" && object.id.startsWith(config.url) typeof object.id === "string" && object.id.startsWith(config.url)
@ -11,7 +10,7 @@ export default (object: any, user: { id: User["id"] }) => {
return { return {
type: "Undo", type: "Undo",
...(id ? { id } : {}), ...(id ? { id } : {}),
actor: `${config.url}/users/${user.id}`, actor: `${config.url}/users/${userId}`,
object, object,
published: new Date().toISOString(), published: new Date().toISOString(),
}; };

View file

@ -2,7 +2,7 @@ import { config } from "@/config.js";
import type { ILocalUser } from "@/models/entities/user.js"; import type { ILocalUser } from "@/models/entities/user.js";
import { import {
extractHost, extractHost,
getInternalActor, getInstanceActor,
isAllowedServer, isAllowedServer,
isBlockedServer, isBlockedServer,
isSelfHost, isSelfHost,
@ -112,7 +112,7 @@ export default class Resolver {
} }
if (!this.user) { if (!this.user) {
this.user = await getInternalActor("instance"); this.user = (await getInstanceActor()) as ILocalUser;
} }
apLogger.info( apLogger.info(

View file

@ -9,7 +9,7 @@ import renderKey from "@/remote/activitypub/renderer/key.js";
import { renderPerson } from "@/remote/activitypub/renderer/person.js"; import { renderPerson } from "@/remote/activitypub/renderer/person.js";
import renderEmoji from "@/remote/activitypub/renderer/emoji.js"; import renderEmoji from "@/remote/activitypub/renderer/emoji.js";
import { inbox as processInbox } from "@/queue/index.js"; import { inbox as processInbox } from "@/queue/index.js";
import { fetchMeta, getInternalActor, isSelfHost } from "backend-rs"; import { fetchMeta, getInstanceActor, isSelfHost } from "backend-rs";
import { import {
Notes, Notes,
Users, Users,
@ -242,8 +242,8 @@ router.get("/notes/:note", async (ctx, next) => {
ctx.body = renderActivity(await renderNote(note, false)); ctx.body = renderActivity(await renderNote(note, false));
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else { } else {
ctx.set("Cache-Control", "public, max-age=180"); ctx.set("Cache-Control", "public, max-age=180");
@ -272,8 +272,8 @@ router.get("/notes/:note/activity", async (ctx) => {
} }
ctx.body = renderActivity(await packActivity(note)); ctx.body = renderActivity(await packActivity(note));
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else { } else {
ctx.set("Cache-Control", "public, max-age=180"); ctx.set("Cache-Control", "public, max-age=180");
@ -295,7 +295,7 @@ router.get("/users/:user/collections/featured", Featured);
// publickey // publickey
router.get("/users/:user/publickey", async (ctx) => { router.get("/users/:user/publickey", async (ctx) => {
const instanceActor = await getInternalActor("instance"); const instanceActor = (await getInstanceActor()) as ILocalUser;
if (ctx.params.user === instanceActor.id) { if (ctx.params.user === instanceActor.id) {
ctx.body = renderActivity( ctx.body = renderActivity(
renderKey(instanceActor, await getUserKeypair(instanceActor.id)), renderKey(instanceActor, await getUserKeypair(instanceActor.id)),
@ -327,8 +327,8 @@ router.get("/users/:user/publickey", async (ctx) => {
if (Users.isLocalUser(user)) { if (Users.isLocalUser(user)) {
ctx.body = renderActivity(renderKey(user, keypair)); ctx.body = renderActivity(renderKey(user, keypair));
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else { } else {
ctx.set("Cache-Control", "public, max-age=180"); ctx.set("Cache-Control", "public, max-age=180");
@ -347,8 +347,8 @@ async function userInfo(ctx: Router.RouterContext, user: User | null) {
} }
ctx.body = renderActivity(await renderPerson(user as ILocalUser)); ctx.body = renderActivity(await renderPerson(user as ILocalUser));
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else { } else {
ctx.set("Cache-Control", "public, max-age=180"); ctx.set("Cache-Control", "public, max-age=180");
@ -359,7 +359,7 @@ async function userInfo(ctx: Router.RouterContext, user: User | null) {
router.get("/users/:user", async (ctx, next) => { router.get("/users/:user", async (ctx, next) => {
if (!isActivityPubReq(ctx)) return await next(); if (!isActivityPubReq(ctx)) return await next();
const instanceActor = await getInternalActor("instance"); const instanceActor = (await getInstanceActor()) as ILocalUser;
if (ctx.params.user === instanceActor.id) { if (ctx.params.user === instanceActor.id) {
await userInfo(ctx, instanceActor); await userInfo(ctx, instanceActor);
return; return;
@ -386,7 +386,7 @@ router.get("/@:user", async (ctx, next) => {
if (!isActivityPubReq(ctx)) return await next(); if (!isActivityPubReq(ctx)) return await next();
if (ctx.params.user === "instance.actor") { if (ctx.params.user === "instance.actor") {
const instanceActor = await getInternalActor("instance"); const instanceActor = (await getInstanceActor()) as ILocalUser;
await userInfo(ctx, instanceActor); await userInfo(ctx, instanceActor);
return; return;
} }
@ -407,7 +407,7 @@ router.get("/@:user", async (ctx, next) => {
}); });
router.get("/actor", async (ctx, _next) => { router.get("/actor", async (ctx, _next) => {
const instanceActor = await getInternalActor("instance"); const instanceActor = (await getInstanceActor()) as ILocalUser;
await userInfo(ctx, instanceActor); await userInfo(ctx, instanceActor);
}); });
//#endregion //#endregion
@ -431,8 +431,8 @@ router.get("/emojis/:emoji", async (ctx) => {
} }
ctx.body = renderActivity(renderEmoji(emoji)); ctx.body = renderActivity(renderEmoji(emoji));
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else { } else {
ctx.set("Cache-Control", "public, max-age=180"); ctx.set("Cache-Control", "public, max-age=180");
@ -463,8 +463,8 @@ router.get("/likes/:like", async (ctx) => {
} }
ctx.body = renderActivity(await renderLike(reaction, note)); ctx.body = renderActivity(await renderLike(reaction, note));
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else { } else {
ctx.set("Cache-Control", "public, max-age=180"); ctx.set("Cache-Control", "public, max-age=180");
@ -501,8 +501,8 @@ router.get(
} }
ctx.body = renderActivity(renderFollow(follower, followee)); ctx.body = renderActivity(renderFollow(follower, followee));
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else { } else {
ctx.set("Cache-Control", "public, max-age=180"); ctx.set("Cache-Control", "public, max-age=180");
@ -544,8 +544,8 @@ router.get("/follows/:followRequestId", async (ctx: Router.RouterContext) => {
return; return;
} }
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else { } else {
ctx.set("Cache-Control", "public, max-age=180"); ctx.set("Cache-Control", "public, max-age=180");

View file

@ -57,8 +57,8 @@ export default async (ctx: Router.RouterContext) => {
ctx.body = renderActivity(rendered); ctx.body = renderActivity(rendered);
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else { } else {
ctx.set("Cache-Control", "public, max-age=180"); ctx.set("Cache-Control", "public, max-age=180");

View file

@ -110,8 +110,8 @@ export default async (ctx: Router.RouterContext) => {
ctx.body = renderActivity(rendered); ctx.body = renderActivity(rendered);
setResponseType(ctx); setResponseType(ctx);
} }
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else { } else {
ctx.set("Cache-Control", "public, max-age=180"); ctx.set("Cache-Control", "public, max-age=180");

View file

@ -110,8 +110,8 @@ export default async (ctx: Router.RouterContext) => {
ctx.body = renderActivity(rendered); ctx.body = renderActivity(rendered);
setResponseType(ctx); setResponseType(ctx);
} }
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else { } else {
ctx.set("Cache-Control", "public, max-age=180"); ctx.set("Cache-Control", "public, max-age=180");

View file

@ -117,8 +117,8 @@ export default async (ctx: Router.RouterContext) => {
setResponseType(ctx); setResponseType(ctx);
} }
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else { } else {
ctx.set("Cache-Control", "public, max-age=180"); ctx.set("Cache-Control", "public, max-age=180");

View file

@ -117,9 +117,9 @@ export default async (
} }
// private mode // private mode
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if ( if (
meta.privateMode && instanceMeta.privateMode &&
ep.meta.requireCredentialPrivateMode && ep.meta.requireCredentialPrivateMode &&
user == null user == null
) { ) {

View file

@ -1,9 +1,10 @@
import { publishMainStream } from "@/services/stream.js";
import { import {
publishToChatStream, publishToChatStream,
publishToGroupChatStream, publishToGroupChatStream,
publishToChatIndexStream, publishToChatIndexStream,
sendPushNotification, sendPushNotification,
publishToMainStream,
Event,
} from "backend-rs"; } from "backend-rs";
import type { User, IRemoteUser } from "@/models/entities/user.js"; import type { User, IRemoteUser } from "@/models/entities/user.js";
import type { MessagingMessage } from "@/models/entities/messaging-message.js"; import type { MessagingMessage } from "@/models/entities/messaging-message.js";
@ -61,7 +62,7 @@ export async function readUserMessagingMessage(
if (!(await Users.getHasUnreadMessagingMessage(userId))) { if (!(await Users.getHasUnreadMessagingMessage(userId))) {
// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行 // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
publishMainStream(userId, "readAllMessagingMessages"); await publishToMainStream(userId, Event.ReadAllChats, {});
await sendPushNotification(userId, "readAllChats", {}); await sendPushNotification(userId, "readAllChats", {});
} else { } else {
// そのユーザーとのメッセージで未読がなければイベント発行 // そのユーザーとのメッセージで未読がなければイベント発行
@ -135,7 +136,7 @@ export async function readGroupMessagingMessage(
if (!(await Users.getHasUnreadMessagingMessage(userId))) { if (!(await Users.getHasUnreadMessagingMessage(userId))) {
// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行 // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
publishMainStream(userId, "readAllMessagingMessages"); await publishToMainStream(userId, Event.ReadAllChats, {});
await sendPushNotification(userId, "readAllChats", {}); await sendPushNotification(userId, "readAllChats", {});
} else { } else {
// そのグループにおいて未読がなければイベント発行 // そのグループにおいて未読がなければイベント発行
@ -173,10 +174,10 @@ export async function deliverReadActivity(
undefined, undefined,
contents, contents,
); );
deliver(user, renderActivity(collection), recipient.inbox); deliver(user.id, renderActivity(collection), recipient.inbox);
} else { } else {
for (const content of contents) { for (const content of contents) {
deliver(user, renderActivity(content), recipient.inbox); deliver(user.id, renderActivity(content), recipient.inbox);
} }
} }
} }

View file

@ -1,6 +1,5 @@
import { In } from "typeorm"; import { In } from "typeorm";
import { publishMainStream } from "@/services/stream.js"; import { Event, publishToMainStream, sendPushNotification } from "backend-rs";
import { sendPushNotification } from "backend-rs";
import type { User } from "@/models/entities/user.js"; import type { User } from "@/models/entities/user.js";
import type { Notification } from "@/models/entities/notification.js"; import type { Notification } from "@/models/entities/notification.js";
import { Notifications, Users } from "@/models/index.js"; import { Notifications, Users } from "@/models/index.js";
@ -46,7 +45,7 @@ export async function readNotificationByQuery(
} }
function postReadAllNotifications(userId: User["id"]) { function postReadAllNotifications(userId: User["id"]) {
publishMainStream(userId, "readAllNotifications"); publishToMainStream(userId, Event.ReadAllNotifications, {});
return sendPushNotification(userId, "readAllNotifications", {}); return sendPushNotification(userId, "readAllNotifications", {});
} }
@ -54,7 +53,7 @@ function postReadNotifications(
userId: User["id"], userId: User["id"],
notificationIds: Notification["id"][], notificationIds: Notification["id"][],
) { ) {
publishMainStream(userId, "readNotifications", notificationIds); publishToMainStream(userId, Event.ReadNotifications, notificationIds);
return sendPushNotification(userId, "readNotifications", { return sendPushNotification(userId, "readNotifications", {
notificationIds, notificationIds,
}); });

View file

@ -3,8 +3,7 @@ import type Koa from "koa";
import { config } from "@/config.js"; import { config } from "@/config.js";
import type { ILocalUser } from "@/models/entities/user.js"; import type { ILocalUser } from "@/models/entities/user.js";
import { Signins } from "@/models/index.js"; import { Signins } from "@/models/index.js";
import { genIdAt } from "backend-rs"; import { Event, genIdAt, publishToMainStream } from "backend-rs";
import { publishMainStream } from "@/services/stream.js";
export default function (ctx: Koa.Context, user: ILocalUser, redirect = false) { export default function (ctx: Koa.Context, user: ILocalUser, redirect = false) {
if (redirect) { if (redirect) {
@ -40,6 +39,6 @@ export default function (ctx: Koa.Context, user: ILocalUser, redirect = false) {
}).then((x) => Signins.findOneByOrFail(x.identifiers[0])); }).then((x) => Signins.findOneByOrFail(x.identifiers[0]));
// Publish signin event // Publish signin event
publishMainStream(user.id, "signin", await Signins.pack(record)); publishToMainStream(user.id, Event.Signin, await Signins.pack(record));
})(); })();
} }

View file

@ -1,7 +1,7 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { Users } from "@/models/index.js"; import { Users } from "@/models/index.js";
import { doPostSuspend } from "@/services/suspend-user.js"; import { doPostSuspend } from "@/services/suspend-user.js";
import { publishUserEvent } from "@/services/stream.js"; import { publishToUserStream, UserEvent } from "backend-rs";
import { createDeleteAccountJob } from "@/queue/index.js"; import { createDeleteAccountJob } from "@/queue/index.js";
export const meta = { export const meta = {
@ -53,6 +53,6 @@ export default define(meta, paramDef, async (ps, me) => {
if (Users.isLocalUser(user)) { if (Users.isLocalUser(user)) {
// Terminate streaming // Terminate streaming
publishUserEvent(user.id, "terminate", {}); await publishToUserStream(user.id, UserEvent.Disconnect, {});
} }
}); });

View file

@ -1,5 +1,5 @@
import { Users, UserProfiles } from "@/models/index.js"; import { Users, UserProfiles } from "@/models/index.js";
import { publishMainStream } from "@/services/stream.js"; import { Event, publishToMainStream } from "backend-rs";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
export const meta = { export const meta = {
@ -36,5 +36,5 @@ export default define(meta, paramDef, async (ps) => {
includeSecrets: true, includeSecrets: true,
}); });
publishMainStream(user.id, "meUpdated", iObj); publishToMainStream(user.id, Event.Me, iObj);
}); });

View file

@ -1,5 +1,5 @@
import { Users, UserProfiles, UserSecurityKeys } from "@/models/index.js"; import { Users, UserProfiles, UserSecurityKeys } from "@/models/index.js";
import { publishMainStream } from "@/services/stream.js"; import { Event, publishToMainStream } from "backend-rs";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
export const meta = { export const meta = {
@ -38,5 +38,5 @@ export default define(meta, paramDef, async (ps) => {
includeSecrets: true, includeSecrets: true,
}); });
publishMainStream(user.id, "meUpdated", iObj); publishToMainStream(user.id, Event.Me, iObj);
}); });

View file

@ -1,7 +1,7 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { Users } from "@/models/index.js"; import { Users } from "@/models/index.js";
import { insertModerationLog } from "@/services/insert-moderation-log.js"; import { insertModerationLog } from "@/services/insert-moderation-log.js";
import { publishInternalEvent } from "@/services/stream.js"; import { InternalEvent, publishToInternalStream } from "backend-rs";
export const meta = { export const meta = {
tags: ["admin"], tags: ["admin"],
@ -33,7 +33,7 @@ export default define(meta, paramDef, async (ps, me) => {
driveCapacityOverrideMb: ps.overrideMb, driveCapacityOverrideMb: ps.overrideMb,
}); });
publishInternalEvent("localUserUpdated", { publishToInternalStream(InternalEvent.LocalUser, {
id: user.id, id: user.id,
}); });

View file

@ -2,7 +2,7 @@ import define from "@/server/api/define.js";
import { Emojis, DriveFiles } from "@/models/index.js"; import { Emojis, DriveFiles } from "@/models/index.js";
import { import {
type ImageSize, type ImageSize,
genId, genIdAt,
getImageSizeFromUrl, getImageSizeFromUrl,
publishToBroadcastStream, publishToBroadcastStream,
} from "backend-rs"; } from "backend-rs";
@ -36,7 +36,31 @@ export const meta = {
export const paramDef = { export const paramDef = {
type: "object", type: "object",
properties: { properties: {
fileId: { type: "string", format: "misskey:id" }, fileId: {
type: "string",
format: "misskey:id",
nullable: false,
},
name: {
type: "string",
nullable: false,
},
category: {
type: "string",
nullable: false,
},
aliases: {
type: "array",
nullable: false,
items: {
type: "string",
nullable: false,
},
},
license: {
type: "string",
nullable: false,
},
}, },
required: ["fileId"], required: ["fileId"],
} as const; } as const;
@ -50,9 +74,12 @@ export default define(meta, paramDef, async (ps, me) => {
if (file == null) throw new ApiError(meta.errors.noSuchFile); if (file == null) throw new ApiError(meta.errors.noSuchFile);
const name = file.name.split(".")[0].match(/^[a-z0-9_]+$/) const name =
? file.name.split(".")[0] ps.name != null
: `_${rndstr("a-z0-9", 8)}_`; ? ps.name
: file.name.split(".")[0].match(/^[a-z0-9_]+$/)
? file.name.split(".")[0]
: `_${rndstr("a-z0-9", 8)}_`;
let size: ImageSize | null = null; let size: ImageSize | null = null;
try { try {
@ -62,17 +89,19 @@ export default define(meta, paramDef, async (ps, me) => {
apiLogger.debug(inspect(err)); apiLogger.debug(inspect(err));
} }
const now = new Date();
const emoji = await Emojis.insert({ const emoji = await Emojis.insert({
id: genId(), id: genIdAt(now),
updatedAt: new Date(), updatedAt: now,
name: name, name: name,
category: null, category: ps.category ?? null,
host: null, host: null,
aliases: [], aliases: ps.aliases ?? [],
originalUrl: file.url, originalUrl: file.url,
publicUrl: file.webpublicUrl ?? file.url, publicUrl: file.webpublicUrl ?? file.url,
type: file.webpublicType ?? file.type, type: file.webpublicType ?? file.type,
license: null, license: ps.license ?? null,
width: size?.width || null, width: size?.width || null,
height: size?.height || null, height: size?.height || null,
}).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); }).then((x) => Emojis.findOneByOrFail(x.identifiers[0]));

View file

@ -470,97 +470,98 @@ export const paramDef = {
} as const; } as const;
export default define(meta, paramDef, async () => { export default define(meta, paramDef, async () => {
const instance = await fetchMeta(); const instanceMeta = await fetchMeta();
return { return {
maintainerName: instance.maintainerName, maintainerName: instanceMeta.maintainerName,
maintainerEmail: instance.maintainerEmail, maintainerEmail: instanceMeta.maintainerEmail,
version: config.version, version: config.version,
name: instance.name, name: instanceMeta.name,
uri: config.url, uri: config.url,
description: instance.description, description: instanceMeta.description,
langs: instance.langs, langs: instanceMeta.langs,
tosUrl: instance.tosUrl, tosUrl: instanceMeta.tosUrl,
moreUrls: instance.moreUrls, moreUrls: instanceMeta.moreUrls,
repositoryUrl: instance.repositoryUrl, repositoryUrl: instanceMeta.repositoryUrl,
feedbackUrl: instance.feedbackUrl, feedbackUrl: instanceMeta.feedbackUrl,
disableRegistration: instance.disableRegistration, disableRegistration: instanceMeta.disableRegistration,
disableLocalTimeline: instance.disableLocalTimeline, disableLocalTimeline: instanceMeta.disableLocalTimeline,
disableRecommendedTimeline: instance.disableRecommendedTimeline, disableRecommendedTimeline: instanceMeta.disableRecommendedTimeline,
disableGlobalTimeline: instance.disableGlobalTimeline, disableGlobalTimeline: instanceMeta.disableGlobalTimeline,
enableGuestTimeline: instance.enableGuestTimeline, enableGuestTimeline: instanceMeta.enableGuestTimeline,
driveCapacityPerLocalUserMb: instance.localDriveCapacityMb, driveCapacityPerLocalUserMb: instanceMeta.localDriveCapacityMb,
driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb, driveCapacityPerRemoteUserMb: instanceMeta.remoteDriveCapacityMb,
antennaLimit: instance.antennaLimit, antennaLimit: instanceMeta.antennaLimit,
emailRequiredForSignup: instance.emailRequiredForSignup, emailRequiredForSignup: instanceMeta.emailRequiredForSignup,
enableHcaptcha: instance.enableHcaptcha, enableHcaptcha: instanceMeta.enableHcaptcha,
hcaptchaSiteKey: instance.hcaptchaSiteKey, hcaptchaSiteKey: instanceMeta.hcaptchaSiteKey,
enableRecaptcha: instance.enableRecaptcha, enableRecaptcha: instanceMeta.enableRecaptcha,
recaptchaSiteKey: instance.recaptchaSiteKey, recaptchaSiteKey: instanceMeta.recaptchaSiteKey,
swPublickey: instance.swPublicKey, swPublickey: instanceMeta.swPublicKey,
themeColor: instance.themeColor, themeColor: instanceMeta.themeColor,
mascotImageUrl: instance.mascotImageUrl, mascotImageUrl: instanceMeta.mascotImageUrl,
bannerUrl: instance.bannerUrl, bannerUrl: instanceMeta.bannerUrl,
errorImageUrl: instance.errorImageUrl, errorImageUrl: instanceMeta.errorImageUrl,
iconUrl: instance.iconUrl, iconUrl: instanceMeta.iconUrl,
backgroundImageUrl: instance.backgroundImageUrl, backgroundImageUrl: instanceMeta.backgroundImageUrl,
logoImageUrl: instance.logoImageUrl, logoImageUrl: instanceMeta.logoImageUrl,
maxNoteTextLength: config.maxNoteLength, // for backward compatibility maxNoteTextLength: config.maxNoteLength, // for backward compatibility
maxCaptionTextLength: config.maxCaptionLength, maxCaptionTextLength: config.maxCaptionLength,
defaultLightTheme: instance.defaultLightTheme, defaultLightTheme: instanceMeta.defaultLightTheme,
defaultDarkTheme: instance.defaultDarkTheme, defaultDarkTheme: instanceMeta.defaultDarkTheme,
enableEmail: instance.enableEmail, enableEmail: instanceMeta.enableEmail,
enableServiceWorker: instance.enableServiceWorker, enableServiceWorker: instanceMeta.enableServiceWorker,
translatorAvailable: translatorAvailable:
instance.deeplAuthKey != null || instance.libreTranslateApiUrl != null, instanceMeta.deeplAuthKey != null ||
pinnedPages: instance.pinnedPages, instanceMeta.libreTranslateApiUrl != null,
pinnedClipId: instance.pinnedClipId, pinnedPages: instanceMeta.pinnedPages,
cacheRemoteFiles: instance.cacheRemoteFiles, pinnedClipId: instanceMeta.pinnedClipId,
markLocalFilesNsfwByDefault: instance.markLocalFilesNsfwByDefault, cacheRemoteFiles: instanceMeta.cacheRemoteFiles,
defaultReaction: instance.defaultReaction, markLocalFilesNsfwByDefault: instanceMeta.markLocalFilesNsfwByDefault,
recommendedInstances: instance.recommendedInstances, defaultReaction: instanceMeta.defaultReaction,
pinnedUsers: instance.pinnedUsers, recommendedInstances: instanceMeta.recommendedInstances,
customMOTD: instance.customMotd, pinnedUsers: instanceMeta.pinnedUsers,
customSplashIcons: instance.customSplashIcons, customMOTD: instanceMeta.customMotd,
hiddenTags: instance.hiddenTags, customSplashIcons: instanceMeta.customSplashIcons,
blockedHosts: instance.blockedHosts, hiddenTags: instanceMeta.hiddenTags,
silencedHosts: instance.silencedHosts, blockedHosts: instanceMeta.blockedHosts,
allowedHosts: instance.allowedHosts, silencedHosts: instanceMeta.silencedHosts,
privateMode: instance.privateMode, allowedHosts: instanceMeta.allowedHosts,
secureMode: instance.secureMode, privateMode: instanceMeta.privateMode,
hcaptchaSecretKey: instance.hcaptchaSecretKey, secureMode: instanceMeta.secureMode,
recaptchaSecretKey: instance.recaptchaSecretKey, hcaptchaSecretKey: instanceMeta.hcaptchaSecretKey,
proxyAccountId: instance.proxyAccountId, recaptchaSecretKey: instanceMeta.recaptchaSecretKey,
summalyProxy: instance.summalyProxy, proxyAccountId: instanceMeta.proxyAccountId,
email: instance.email, summalyProxy: instanceMeta.summalyProxy,
smtpSecure: instance.smtpSecure, email: instanceMeta.email,
smtpHost: instance.smtpHost, smtpSecure: instanceMeta.smtpSecure,
smtpPort: instance.smtpPort, smtpHost: instanceMeta.smtpHost,
smtpUser: instance.smtpUser, smtpPort: instanceMeta.smtpPort,
smtpPass: instance.smtpPass, smtpUser: instanceMeta.smtpUser,
swPrivateKey: instance.swPrivateKey, smtpPass: instanceMeta.smtpPass,
useObjectStorage: instance.useObjectStorage, swPrivateKey: instanceMeta.swPrivateKey,
objectStorageBaseUrl: instance.objectStorageBaseUrl, useObjectStorage: instanceMeta.useObjectStorage,
objectStorageBucket: instance.objectStorageBucket, objectStorageBaseUrl: instanceMeta.objectStorageBaseUrl,
objectStoragePrefix: instance.objectStoragePrefix, objectStorageBucket: instanceMeta.objectStorageBucket,
objectStorageEndpoint: instance.objectStorageEndpoint, objectStoragePrefix: instanceMeta.objectStoragePrefix,
objectStorageRegion: instance.objectStorageRegion, objectStorageEndpoint: instanceMeta.objectStorageEndpoint,
objectStoragePort: instance.objectStoragePort, objectStorageRegion: instanceMeta.objectStorageRegion,
objectStorageAccessKey: instance.objectStorageAccessKey, objectStoragePort: instanceMeta.objectStoragePort,
objectStorageSecretKey: instance.objectStorageSecretKey, objectStorageAccessKey: instanceMeta.objectStorageAccessKey,
objectStorageUseSSL: instance.objectStorageUseSsl, objectStorageSecretKey: instanceMeta.objectStorageSecretKey,
objectStorageUseProxy: instance.objectStorageUseProxy, objectStorageUseSSL: instanceMeta.objectStorageUseSsl,
objectStorageSetPublicRead: instance.objectStorageSetPublicRead, objectStorageUseProxy: instanceMeta.objectStorageUseProxy,
objectStorageS3ForcePathStyle: instance.objectStorageS3ForcePathStyle, objectStorageSetPublicRead: instanceMeta.objectStorageSetPublicRead,
deeplAuthKey: instance.deeplAuthKey, objectStorageS3ForcePathStyle: instanceMeta.objectStorageS3ForcePathStyle,
deeplIsPro: instance.deeplIsPro, deeplAuthKey: instanceMeta.deeplAuthKey,
libreTranslateApiUrl: instance.libreTranslateApiUrl, deeplIsPro: instanceMeta.deeplIsPro,
libreTranslateApiKey: instance.libreTranslateApiKey, libreTranslateApiUrl: instanceMeta.libreTranslateApiUrl,
enableIpLogging: instance.enableIpLogging, libreTranslateApiKey: instanceMeta.libreTranslateApiKey,
enableActiveEmailValidation: instance.enableActiveEmailValidation, enableIpLogging: instanceMeta.enableIpLogging,
experimentalFeatures: instance.experimentalFeatures, enableActiveEmailValidation: instanceMeta.enableActiveEmailValidation,
enableServerMachineStats: instance.enableServerMachineStats, experimentalFeatures: instanceMeta.experimentalFeatures,
enableIdenticonGeneration: instance.enableIdenticonGeneration, enableServerMachineStats: instanceMeta.enableServerMachineStats,
donationLink: instance.donationLink, enableIdenticonGeneration: instanceMeta.enableIdenticonGeneration,
donationLink: instanceMeta.donationLink,
}; };
}); });

View file

@ -1,6 +1,6 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { Users } from "@/models/index.js"; import { Users } from "@/models/index.js";
import { publishInternalEvent } from "@/services/stream.js"; import { InternalEvent, publishToInternalStream } from "backend-rs";
export const meta = { export const meta = {
tags: ["admin"], tags: ["admin"],
@ -32,7 +32,7 @@ export default define(meta, paramDef, async (ps) => {
isModerator: true, isModerator: true,
}); });
publishInternalEvent("userChangeModeratorState", { publishToInternalStream(InternalEvent.Moderator, {
id: user.id, id: user.id,
isModerator: true, isModerator: true,
}); });

View file

@ -1,6 +1,6 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { Users } from "@/models/index.js"; import { Users } from "@/models/index.js";
import { publishInternalEvent } from "@/services/stream.js"; import { InternalEvent, publishToInternalStream } from "backend-rs";
export const meta = { export const meta = {
tags: ["admin"], tags: ["admin"],
@ -28,7 +28,7 @@ export default define(meta, paramDef, async (ps) => {
isModerator: false, isModerator: false,
}); });
publishInternalEvent("userChangeModeratorState", { publishToInternalStream(InternalEvent.Moderator, {
id: user.id, id: user.id,
isModerator: false, isModerator: false,
}); });

View file

@ -1,9 +1,10 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { AbuseUserReports, Users } from "@/models/index.js"; import { AbuseUserReports, Users } from "@/models/index.js";
import { getInternalActor } from "backend-rs"; import { getInstanceActor } from "backend-rs";
import { deliver } from "@/queue/index.js"; import { deliver } from "@/queue/index.js";
import { renderActivity } from "@/remote/activitypub/renderer/index.js"; import { renderActivity } from "@/remote/activitypub/renderer/index.js";
import { renderFlag } from "@/remote/activitypub/renderer/flag.js"; import { renderFlag } from "@/remote/activitypub/renderer/flag.js";
import { ILocalUser } from "@/models/entities/user";
export const meta = { export const meta = {
tags: ["admin"], tags: ["admin"],
@ -29,11 +30,11 @@ export default define(meta, paramDef, async (ps, me) => {
} }
if (ps.forward && report.targetUserHost != null) { if (ps.forward && report.targetUserHost != null) {
const actor = await getInternalActor("instance"); const actor = (await getInstanceActor()) as ILocalUser;
const targetUser = await Users.findOneByOrFail({ id: report.targetUserId }); const targetUser = await Users.findOneByOrFail({ id: report.targetUserId });
deliver( deliver(
actor, actor.id,
renderActivity(renderFlag(actor, [targetUser.uri!], report.comment)), renderActivity(renderFlag(actor, [targetUser.uri!], report.comment)),
targetUser.inbox, targetUser.inbox,
); );

View file

@ -1,6 +1,5 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { Users } from "@/models/index.js"; import { Users } from "@/models/index.js";
import { publishInternalEvent } from "@/services/stream.js";
import type { EmojiModPerm } from "@/models/entities/user.js"; import type { EmojiModPerm } from "@/models/entities/user.js";
import { unsafeCast } from "@/prelude/unsafe-cast.js"; import { unsafeCast } from "@/prelude/unsafe-cast.js";
@ -36,9 +35,4 @@ export default define(meta, paramDef, async (ps) => {
await Users.update(user.id, { await Users.update(user.id, {
emojiModPerm: unsafeCast<EmojiModPerm>(ps.emojiModPerm), emojiModPerm: unsafeCast<EmojiModPerm>(ps.emojiModPerm),
}); });
publishInternalEvent("userChangeModeratorState", {
id: user.id,
isModerator: true,
});
}); });

View file

@ -1,7 +1,7 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { Users } from "@/models/index.js"; import { Users } from "@/models/index.js";
import { insertModerationLog } from "@/services/insert-moderation-log.js"; import { insertModerationLog } from "@/services/insert-moderation-log.js";
import { publishInternalEvent } from "@/services/stream.js"; import { InternalEvent, publishToInternalStream } from "backend-rs";
export const meta = { export const meta = {
tags: ["admin"], tags: ["admin"],
@ -33,7 +33,7 @@ export default define(meta, paramDef, async (ps, me) => {
isSilenced: true, isSilenced: true,
}); });
publishInternalEvent("userChangeSilencedState", { publishToInternalStream(InternalEvent.Silence, {
id: user.id, id: user.id,
isSilenced: true, isSilenced: true,
}); });

View file

@ -4,7 +4,7 @@ import { Users, Followings, Notifications } from "@/models/index.js";
import type { User } from "@/models/entities/user.js"; import type { User } from "@/models/entities/user.js";
import { insertModerationLog } from "@/services/insert-moderation-log.js"; import { insertModerationLog } from "@/services/insert-moderation-log.js";
import { doPostSuspend } from "@/services/suspend-user.js"; import { doPostSuspend } from "@/services/suspend-user.js";
import { publishUserEvent } from "@/services/stream.js"; import { publishToUserStream, UserEvent } from "backend-rs";
export const meta = { export const meta = {
tags: ["admin"], tags: ["admin"],
@ -46,7 +46,7 @@ export default define(meta, paramDef, async (ps, me) => {
// Terminate streaming // Terminate streaming
if (Users.isLocalUser(user)) { if (Users.isLocalUser(user)) {
publishUserEvent(user.id, "terminate", {}); await publishToUserStream(user.id, UserEvent.Disconnect, {});
} }
(async () => { (async () => {

View file

@ -1,7 +1,7 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { Users } from "@/models/index.js"; import { Users } from "@/models/index.js";
import { insertModerationLog } from "@/services/insert-moderation-log.js"; import { insertModerationLog } from "@/services/insert-moderation-log.js";
import { publishInternalEvent } from "@/services/stream.js"; import { InternalEvent, publishToInternalStream } from "backend-rs";
export const meta = { export const meta = {
tags: ["admin"], tags: ["admin"],
@ -29,7 +29,7 @@ export default define(meta, paramDef, async (ps, me) => {
isSilenced: false, isSilenced: false,
}); });
publishInternalEvent("userChangeSilencedState", { publishToInternalStream(InternalEvent.Silence, {
id: user.id, id: user.id,
isSilenced: false, isSilenced: false,
}); });

View file

@ -1,8 +1,13 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { fetchMeta, genIdAt, updateAntennaCache } from "backend-rs"; import {
fetchMeta,
genIdAt,
InternalEvent,
publishToInternalStream,
updateAntennaCache,
} from "backend-rs";
import { Antennas, UserLists, UserGroupJoinings } from "@/models/index.js"; import { Antennas, UserLists, UserGroupJoinings } from "@/models/index.js";
import { ApiError } from "@/server/api/error.js"; import { ApiError } from "@/server/api/error.js";
import { publishInternalEvent } from "@/services/stream.js";
export const meta = { export const meta = {
tags: ["antennas"], tags: ["antennas"],
@ -123,12 +128,12 @@ export default define(meta, paramDef, async (ps, user) => {
let userList; let userList;
let userGroupJoining; let userGroupJoining;
const instance = await fetchMeta(); const instanceMeta = await fetchMeta();
const antennas = await Antennas.findBy({ const antennas = await Antennas.findBy({
userId: user.id, userId: user.id,
}); });
if (antennas.length >= instance.antennaLimit) { if (antennas.length >= instanceMeta.antennaLimit) {
throw new ApiError(meta.errors.tooManyAntennas); throw new ApiError(meta.errors.tooManyAntennas);
} }
@ -172,7 +177,7 @@ export default define(meta, paramDef, async (ps, user) => {
notify: ps.notify, notify: ps.notify,
}).then((x) => Antennas.findOneByOrFail(x.identifiers[0])); }).then((x) => Antennas.findOneByOrFail(x.identifiers[0]));
publishInternalEvent("antennaCreated", antenna); await publishToInternalStream(InternalEvent.AntennaCreated, antenna);
await updateAntennaCache(); await updateAntennaCache();
return await Antennas.pack(antenna); return await Antennas.pack(antenna);

View file

@ -1,8 +1,11 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js"; import { ApiError } from "@/server/api/error.js";
import { Antennas } from "@/models/index.js"; import { Antennas } from "@/models/index.js";
import { publishInternalEvent } from "@/services/stream.js"; import {
import { updateAntennaCache } from "backend-rs"; InternalEvent,
publishToInternalStream,
updateAntennaCache,
} from "backend-rs";
export const meta = { export const meta = {
tags: ["antennas"], tags: ["antennas"],
@ -40,6 +43,6 @@ export default define(meta, paramDef, async (ps, user) => {
await Antennas.delete(antenna.id); await Antennas.delete(antenna.id);
publishInternalEvent("antennaDeleted", antenna); await publishToInternalStream(InternalEvent.AntennaDeleted, antenna);
await updateAntennaCache(); await updateAntennaCache();
}); });

View file

@ -1,8 +1,11 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js"; import { ApiError } from "@/server/api/error.js";
import { Antennas, UserLists, UserGroupJoinings } from "@/models/index.js"; import { Antennas, UserLists, UserGroupJoinings } from "@/models/index.js";
import { publishInternalEvent } from "@/services/stream.js"; import {
import { updateAntennaCache } from "backend-rs"; InternalEvent,
publishToInternalStream,
updateAntennaCache,
} from "backend-rs";
export const meta = { export const meta = {
tags: ["antennas"], tags: ["antennas"],
@ -163,8 +166,8 @@ export default define(meta, paramDef, async (ps, user) => {
notify: ps.notify, notify: ps.notify,
}); });
publishInternalEvent( await publishToInternalStream(
"antennaUpdated", InternalEvent.AntennaUpdated,
await Antennas.findOneByOrFail({ id: antenna.id }), await Antennas.findOneByOrFail({ id: antenna.id }),
); );
await updateAntennaCache(); await updateAntennaCache();

View file

@ -1,8 +1,7 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js"; import { ApiError } from "@/server/api/error.js";
import { Channels, ChannelFollowings } from "@/models/index.js"; import { Channels, ChannelFollowings } from "@/models/index.js";
import { genIdAt } from "backend-rs"; import { genIdAt, publishToUserStream, UserEvent } from "backend-rs";
import { publishUserEvent } from "@/services/stream.js";
export const meta = { export const meta = {
tags: ["channels"], tags: ["channels"],
@ -46,5 +45,5 @@ export default define(meta, paramDef, async (ps, user) => {
followeeId: channel.id, followeeId: channel.id,
}); });
publishUserEvent(user.id, "followChannel", channel); await publishToUserStream(user.id, UserEvent.FollowChannel, channel);
}); });

View file

@ -1,7 +1,7 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js"; import { ApiError } from "@/server/api/error.js";
import { Channels, ChannelFollowings } from "@/models/index.js"; import { Channels, ChannelFollowings } from "@/models/index.js";
import { publishUserEvent } from "@/services/stream.js"; import { publishToUserStream, UserEvent } from "backend-rs";
export const meta = { export const meta = {
tags: ["channels"], tags: ["channels"],
@ -41,5 +41,5 @@ export default define(meta, paramDef, async (ps, user) => {
followeeId: channel.id, followeeId: channel.id,
}); });
publishUserEvent(user.id, "unfollowChannel", channel); await publishToUserStream(user.id, UserEvent.UnfollowChannel, channel);
}); });

View file

@ -27,7 +27,7 @@ export const paramDef = {
} as const; } as const;
export default define(meta, paramDef, async () => { export default define(meta, paramDef, async () => {
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
const motd = await Promise.all(meta.customMotd.map((x) => x)); const motd = await Promise.all(instanceMeta.customMotd.map((x) => x));
return motd; return motd;
}); });

View file

@ -27,7 +27,7 @@ export const paramDef = {
} as const; } as const;
export default define(meta, paramDef, async () => { export default define(meta, paramDef, async () => {
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
const icons = await Promise.all(meta.customSplashIcons.map((x) => x)); const icons = await Promise.all(instanceMeta.customSplashIcons.map((x) => x));
return icons; return icons;
}); });

View file

@ -35,7 +35,7 @@ export const paramDef = {
} as const; } as const;
export default define(meta, paramDef, async (ps, user) => { export default define(meta, paramDef, async (ps, user) => {
const instance = await fetchMeta(); const instanceMeta = await fetchMeta();
// Calculate drive usage // Calculate drive usage
const usage = await DriveFiles.calcDriveUsageOf(user.id); const usage = await DriveFiles.calcDriveUsageOf(user.id);
@ -44,7 +44,7 @@ export default define(meta, paramDef, async (ps, user) => {
capacity: capacity:
1024 * 1024 *
1024 * 1024 *
(user.driveCapacityOverrideMb || instance.localDriveCapacityMb), (user.driveCapacityOverrideMb || instanceMeta.localDriveCapacityMb),
usage: usage, usage: usage,
}; };
}); });

View file

@ -1,7 +1,7 @@
import { uploadFromUrl } from "@/services/drive/upload-from-url.js"; import { uploadFromUrl } from "@/services/drive/upload-from-url.js";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { DriveFiles } from "@/models/index.js"; import { DriveFiles } from "@/models/index.js";
import { publishMainStream } from "@/services/stream.js"; import { Event, publishToMainStream } from "backend-rs";
import { HOUR } from "@/const.js"; import { HOUR } from "@/const.js";
export const meta = { export const meta = {
@ -24,6 +24,7 @@ export const paramDef = {
type: "object", type: "object",
properties: { properties: {
url: { type: "string" }, url: { type: "string" },
name: { type: "string" },
folderId: { folderId: {
type: "string", type: "string",
format: "misskey:id", format: "misskey:id",
@ -41,6 +42,7 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, user) => { export default define(meta, paramDef, async (ps, user) => {
uploadFromUrl({ uploadFromUrl({
url: ps.url, url: ps.url,
name: ps.name,
user, user,
folderId: ps.folderId, folderId: ps.folderId,
sensitive: ps.isSensitive, sensitive: ps.isSensitive,
@ -48,7 +50,7 @@ export default define(meta, paramDef, async (ps, user) => {
comment: ps.comment, comment: ps.comment,
}).then((file) => { }).then((file) => {
DriveFiles.pack(file, { self: true }).then((packedFile) => { DriveFiles.pack(file, { self: true }).then((packedFile) => {
publishMainStream(user.id, "urlUploadFinished", { publishToMainStream(user.id, Event.UrlUploadFinished, {
marker: ps.marker, marker: ps.marker,
file: packedFile, file: packedFile,
}); });

View file

@ -37,15 +37,13 @@ export const paramDef = {
required: ["name"], required: ["name"],
} as const; } as const;
export default define(meta, paramDef, async (ps, me) => { export default define(meta, paramDef, async (ps) => {
const emoji = await Emojis.findOne({ const emoji = await Emojis.findOneBy({
where: { name: ps.name,
name: ps.name, host: IsNull(),
host: IsNull(),
},
}); });
if (!emoji) { if (emoji == null) {
throw new ApiError(meta.errors.noSuchEmoji); throw new ApiError(meta.errors.noSuchEmoji);
} }

View file

@ -100,33 +100,33 @@ export default define(meta, paramDef, async (ps, me) => {
} }
if (typeof ps.blocked === "boolean") { if (typeof ps.blocked === "boolean") {
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (ps.blocked) { if (ps.blocked) {
if (meta.blockedHosts.length === 0) { if (instanceMeta.blockedHosts.length === 0) {
return []; return [];
} }
query.andWhere("instance.host IN (:...blocks)", { query.andWhere("instance.host IN (:...blocks)", {
blocks: meta.blockedHosts, blocks: instanceMeta.blockedHosts,
}); });
} else if (meta.blockedHosts.length > 0) { } else if (instanceMeta.blockedHosts.length > 0) {
query.andWhere("instance.host NOT IN (:...blocks)", { query.andWhere("instance.host NOT IN (:...blocks)", {
blocks: meta.blockedHosts, blocks: instanceMeta.blockedHosts,
}); });
} }
} }
if (typeof ps.silenced === "boolean") { if (typeof ps.silenced === "boolean") {
const meta = await fetchMeta(); const instanceMeta = await fetchMeta();
if (ps.silenced) { if (ps.silenced) {
if (meta.silencedHosts.length === 0) { if (instanceMeta.silencedHosts.length === 0) {
return []; return [];
} }
query.andWhere("instance.host IN (:...silences)", { query.andWhere("instance.host IN (:...silences)", {
silences: meta.silencedHosts, silences: instanceMeta.silencedHosts,
}); });
} else if (meta.silencedHosts.length > 0) { } else if (instanceMeta.silencedHosts.length > 0) {
query.andWhere("instance.host NOT IN (:...silences)", { query.andWhere("instance.host NOT IN (:...silences)", {
silences: meta.silencedHosts, silences: instanceMeta.silencedHosts,
}); });
} }
} }

View file

@ -66,8 +66,8 @@ export const paramDef = {
} as const; } as const;
export default define(meta, paramDef, async () => { export default define(meta, paramDef, async () => {
const instance = await fetchMeta(); const instanceMeta = await fetchMeta();
const hiddenTags = instance.hiddenTags.map((t) => normalizeForSearch(t)); const hiddenTags = instanceMeta.hiddenTags.map((t) => normalizeForSearch(t));
const now = new Date(); // 5分単位で丸めた現在日時 const now = new Date(); // 5分単位で丸めた現在日時
now.setMinutes(Math.round(now.getMinutes() / 5) * 5, 0, 0); now.setMinutes(Math.round(now.getMinutes() / 5) * 5, 0, 0);

View file

@ -1,4 +1,4 @@
import { publishMainStream } from "@/services/stream.js"; import { Event, publishToMainStream } from "backend-rs";
import * as OTPAuth from "otpauth"; import * as OTPAuth from "otpauth";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { Users, UserProfiles } from "@/models/index.js"; import { Users, UserProfiles } from "@/models/index.js";
@ -47,5 +47,5 @@ export default define(meta, paramDef, async (ps, user) => {
includeSecrets: true, includeSecrets: true,
}); });
publishMainStream(user.id, "meUpdated", iObj); publishToMainStream(user.id, Event.Me, iObj);
}); });

View file

@ -8,8 +8,7 @@ import {
} from "@/models/index.js"; } from "@/models/index.js";
import { config } from "@/config.js"; import { config } from "@/config.js";
import { procedures, hash } from "@/server/api/2fa.js"; import { procedures, hash } from "@/server/api/2fa.js";
import { publishMainStream } from "@/services/stream.js"; import { Event, publishToMainStream, verifyPassword } from "backend-rs";
import { verifyPassword } from "backend-rs";
const rpIdHashReal = hash(Buffer.from(config.hostname, "utf-8")); const rpIdHashReal = hash(Buffer.from(config.hostname, "utf-8"));
@ -132,9 +131,9 @@ export default define(meta, paramDef, async (ps, user) => {
}); });
// Publish meUpdated event // Publish meUpdated event
publishMainStream( publishToMainStream(
user.id, user.id,
"meUpdated", Event.Me,
await Users.pack(user.id, user, { await Users.pack(user.id, user, {
detail: true, detail: true,
includeSecrets: true, includeSecrets: true,

View file

@ -1,6 +1,6 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { Users, UserProfiles, UserSecurityKeys } from "@/models/index.js"; import { Users, UserProfiles, UserSecurityKeys } from "@/models/index.js";
import { publishMainStream } from "@/services/stream.js"; import { Event, publishToMainStream } from "backend-rs";
import { ApiError } from "@/server/api/error.js"; import { ApiError } from "@/server/api/error.js";
export const meta = { export const meta = {
@ -57,5 +57,5 @@ export default define(meta, paramDef, async (ps, user) => {
includeSecrets: true, includeSecrets: true,
}); });
publishMainStream(user.id, "meUpdated", iObj); publishToMainStream(user.id, Event.Me, iObj);
}); });

View file

@ -1,7 +1,6 @@
import { verifyPassword } from "backend-rs"; import { Event, publishToMainStream, verifyPassword } from "backend-rs";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { UserProfiles, UserSecurityKeys, Users } from "@/models/index.js"; import { UserProfiles, UserSecurityKeys, Users } from "@/models/index.js";
import { publishMainStream } from "@/services/stream.js";
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
@ -54,9 +53,9 @@ export default define(meta, paramDef, async (ps, user) => {
} }
// Publish meUpdated event // Publish meUpdated event
publishMainStream( publishToMainStream(
user.id, user.id,
"meUpdated", Event.Me,
await Users.pack(user.id, user, { await Users.pack(user.id, user, {
detail: true, detail: true,
includeSecrets: true, includeSecrets: true,

View file

@ -1,7 +1,6 @@
import { publishMainStream } from "@/services/stream.js";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { Users, UserProfiles } from "@/models/index.js"; import { Users, UserProfiles } from "@/models/index.js";
import { verifyPassword } from "backend-rs"; import { Event, publishToMainStream, verifyPassword } from "backend-rs";
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
@ -38,5 +37,5 @@ export default define(meta, paramDef, async (ps, user) => {
includeSecrets: true, includeSecrets: true,
}); });
publishMainStream(user.id, "meUpdated", iObj); publishToMainStream(user.id, Event.Me, iObj);
}); });

View file

@ -1,7 +1,7 @@
import { publishMainStream } from "@/services/stream.js";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { Users, UserSecurityKeys } from "@/models/index.js"; import { Users, UserSecurityKeys } from "@/models/index.js";
import { ApiError } from "@/server/api/error.js"; import { ApiError } from "@/server/api/error.js";
import { Event, publishToMainStream } from "backend-rs";
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
@ -54,5 +54,5 @@ export default define(meta, paramDef, async (ps, user) => {
includeSecrets: true, includeSecrets: true,
}); });
publishMainStream(user.id, "meUpdated", iObj); publishToMainStream(user.id, Event.Me, iObj);
}); });

View file

@ -3,8 +3,7 @@ import { Users } from "@/models/index.js";
import { resolveUser } from "@/remote/resolve-user.js"; import { resolveUser } from "@/remote/resolve-user.js";
import acceptAllFollowRequests from "@/services/following/requests/accept-all.js"; import acceptAllFollowRequests from "@/services/following/requests/accept-all.js";
import { publishToFollowers } from "@/services/i/update.js"; import { publishToFollowers } from "@/services/i/update.js";
import { publishMainStream } from "@/services/stream.js"; import { Event, publishToMainStream, stringToAcct } from "backend-rs";
import { stringToAcct } from "backend-rs";
import { DAY } from "@/const.js"; import { DAY } from "@/const.js";
import { apiLogger } from "@/server/api/logger.js"; import { apiLogger } from "@/server/api/logger.js";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
@ -97,7 +96,7 @@ export default define(meta, paramDef, async (ps, user) => {
}); });
// Publish meUpdated event // Publish meUpdated event
publishMainStream(user.id, "meUpdated", iObj); publishToMainStream(user.id, Event.Me, iObj);
if (user.isLocked === false) { if (user.isLocked === false) {
acceptAllFollowRequests(user); acceptAllFollowRequests(user);

View file

@ -1,6 +1,6 @@
import type { User } from "@/models/entities/user.js"; import type { User } from "@/models/entities/user.js";
import { resolveUser } from "@/remote/resolve-user.js"; import { resolveUser } from "@/remote/resolve-user.js";
import { stringToAcct } from "backend-rs"; import { Event, publishToMainStream, stringToAcct } from "backend-rs";
import { DAY } from "@/const.js"; import { DAY } from "@/const.js";
import DeliverManager from "@/remote/activitypub/deliver-manager.js"; import DeliverManager from "@/remote/activitypub/deliver-manager.js";
import { renderActivity } from "@/remote/activitypub/renderer/index.js"; import { renderActivity } from "@/remote/activitypub/renderer/index.js";
@ -12,7 +12,6 @@ import create from "@/services/following/create.js";
import { getUser } from "@/server/api/common/getters.js"; import { getUser } from "@/server/api/common/getters.js";
import { Followings, Users } from "@/models/index.js"; import { Followings, Users } from "@/models/index.js";
import { config } from "@/config.js"; import { config } from "@/config.js";
import { publishMainStream } from "@/services/stream.js";
import { inspect } from "node:util"; import { inspect } from "node:util";
export const meta = { export const meta = {
@ -134,7 +133,7 @@ export default define(meta, paramDef, async (ps, user) => {
dm.execute(); dm.execute();
// Publish meUpdated event // Publish meUpdated event
publishMainStream(user.id, "meUpdated", iObj); publishToMainStream(user.id, Event.Me, iObj);
const followings = await Followings.findBy({ const followings = await Followings.findBy({
followeeId: user.id, followeeId: user.id,

View file

@ -1,4 +1,4 @@
import { publishMainStream } from "@/services/stream.js"; import { Event, publishToMainStream } from "backend-rs";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { MessagingMessages, UserGroupJoinings } from "@/models/index.js"; import { MessagingMessages, UserGroupJoinings } from "@/models/index.js";
@ -16,7 +16,7 @@ export const paramDef = {
required: [], required: [],
} as const; } as const;
export default define(meta, paramDef, async (ps, user) => { export default define(meta, paramDef, async (_ps, user) => {
// Update documents // Update documents
await MessagingMessages.update( await MessagingMessages.update(
{ {
@ -44,5 +44,5 @@ export default define(meta, paramDef, async (ps, user) => {
), ),
); );
publishMainStream(user.id, "readAllMessagingMessages"); publishToMainStream(user.id, Event.ReadAllChats, {});
}); });

View file

@ -1,4 +1,4 @@
import { publishMainStream } from "@/services/stream.js"; import { Event, publishToMainStream } from "backend-rs";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { NoteUnreads } from "@/models/index.js"; import { NoteUnreads } from "@/models/index.js";
@ -23,6 +23,6 @@ export default define(meta, paramDef, async (ps, user) => {
}); });
// 全て既読になったイベントを発行 // 全て既読になったイベントを発行
publishMainStream(user.id, "readAllUnreadMentions"); publishToMainStream(user.id, Event.ReadAllMentions, {});
publishMainStream(user.id, "readAllUnreadSpecifiedNotes"); publishToMainStream(user.id, Event.ReadAllDms, {});
}); });

View file

@ -1,8 +1,7 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js"; import { ApiError } from "@/server/api/error.js";
import { genIdAt } from "backend-rs"; import { Event, genIdAt, publishToMainStream } from "backend-rs";
import { AnnouncementReads, Announcements, Users } from "@/models/index.js"; import { AnnouncementReads, Announcements, Users } from "@/models/index.js";
import { publishMainStream } from "@/services/stream.js";
export const meta = { export const meta = {
tags: ["account"], tags: ["account"],
@ -59,6 +58,6 @@ export default define(meta, paramDef, async (ps, user) => {
}); });
if (!(await Users.getHasUnreadAnnouncement(user.id))) { if (!(await Users.getHasUnreadAnnouncement(user.id))) {
publishMainStream(user.id, "readAllAnnouncements"); publishToMainStream(user.id, Event.ReadAllAnnouncements, {});
} }
}); });

View file

@ -1,11 +1,15 @@
import {
publishInternalEvent,
publishMainStream,
publishUserEvent,
} from "@/services/stream.js";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { Users, UserProfiles } from "@/models/index.js"; import { Users, UserProfiles } from "@/models/index.js";
import { generateUserToken, verifyPassword } from "backend-rs"; import {
Event,
generateUserToken,
InternalEvent,
publishToInternalStream,
publishToMainStream,
publishToUserStream,
UserEvent,
verifyPassword,
} from "backend-rs";
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
@ -41,15 +45,15 @@ export default define(meta, paramDef, async (ps, user) => {
}); });
// Publish event // Publish event
publishInternalEvent("userTokenRegenerated", { publishToInternalStream(InternalEvent.Token, {
id: user.id, id: user.id,
oldToken, oldToken,
newToken, newToken,
}); });
publishMainStream(user.id, "myTokenRegenerated"); publishToMainStream(user.id, Event.RegenerateMyToken, {});
// Terminate streaming // Terminate streaming
setTimeout(() => { setTimeout(() => {
publishUserEvent(user.id, "terminate", {}); publishToUserStream(user.id, UserEvent.Disconnect, {});
}, 5000); }, 5000);
}); });

View file

@ -1,7 +1,6 @@
import { publishMainStream } from "@/services/stream.js";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { RegistryItems } from "@/models/index.js"; import { RegistryItems } from "@/models/index.js";
import { genIdAt } from "backend-rs"; import { Event, genIdAt, publishToMainStream } from "backend-rs";
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
@ -55,7 +54,7 @@ export default define(meta, paramDef, async (ps, user) => {
} }
// TODO: サードパーティアプリが傍受出来てしまうのでどうにかする // TODO: サードパーティアプリが傍受出来てしまうのでどうにかする
publishMainStream(user.id, "registryUpdated", { publishToMainStream(user.id, Event.Registry, {
scope: ps.scope, scope: ps.scope,
key: ps.key, key: ps.key,
value: ps.value, value: ps.value,

View file

@ -1,6 +1,6 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { AccessTokens } from "@/models/index.js"; import { AccessTokens } from "@/models/index.js";
import { publishUserEvent } from "@/services/stream.js"; import { publishToUserStream, UserEvent } from "backend-rs";
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
@ -26,6 +26,6 @@ export default define(meta, paramDef, async (ps, user) => {
}); });
// Terminate streaming // Terminate streaming
publishUserEvent(user.id, "terminate"); await publishToUserStream(user.id, UserEvent.Disconnect, {});
} }
}); });

View file

@ -1,4 +1,3 @@
import { publishMainStream } from "@/services/stream.js";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import rndstr from "rndstr"; import rndstr from "rndstr";
import { config } from "@/config.js"; import { config } from "@/config.js";
@ -6,7 +5,7 @@ import { Users, UserProfiles } from "@/models/index.js";
import { sendEmail } from "@/services/send-email.js"; import { sendEmail } from "@/services/send-email.js";
import { ApiError } from "@/server/api/error.js"; import { ApiError } from "@/server/api/error.js";
import { validateEmailForAccount } from "@/services/validate-email-for-account.js"; import { validateEmailForAccount } from "@/services/validate-email-for-account.js";
import { verifyPassword } from "backend-rs"; import { Event, publishToMainStream, verifyPassword } from "backend-rs";
import { HOUR } from "@/const.js"; import { HOUR } from "@/const.js";
export const meta = { export const meta = {
@ -72,7 +71,7 @@ export default define(meta, paramDef, async (ps, user) => {
}); });
// Publish meUpdated event // Publish meUpdated event
publishMainStream(user.id, "meUpdated", iObj); publishToMainStream(user.id, Event.Me, iObj);
if (ps.email != null) { if (ps.email != null) {
const code = rndstr("a-z0-9", 16); const code = rndstr("a-z0-9", 16);

View file

@ -1,5 +1,10 @@
import * as mfm from "mfm-js"; import * as mfm from "mfm-js";
import { publishMainStream, publishUserEvent } from "@/services/stream.js"; import {
Event,
publishToMainStream,
publishToUserStream,
UserEvent,
} from "backend-rs";
import acceptAllFollowRequests from "@/services/following/requests/accept-all.js"; import acceptAllFollowRequests from "@/services/following/requests/accept-all.js";
import { publishToFollowers } from "@/services/i/update.js"; import { publishToFollowers } from "@/services/i/update.js";
import { extractCustomEmojisFromMfm } from "@/misc/extract-custom-emojis-from-mfm.js"; import { extractCustomEmojisFromMfm } from "@/misc/extract-custom-emojis-from-mfm.js";
@ -345,10 +350,10 @@ export default define(meta, paramDef, async (ps, _user, token) => {
}); });
// Publish meUpdated event // Publish meUpdated event
publishMainStream(user.id, "meUpdated", iObj); await publishToMainStream(user.id, Event.Me, iObj);
publishUserEvent( await publishToUserStream(
user.id, user.id,
"updateUserProfile", UserEvent.UpdateProfile,
await UserProfiles.findOneBy({ userId: user.id }), await UserProfiles.findOneBy({ userId: user.id }),
); );

View file

@ -1,7 +1,6 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { genIdAt } from "backend-rs"; import { genIdAt, InternalEvent, publishToInternalStream } from "backend-rs";
import { Webhooks } from "@/models/index.js"; import { Webhooks } from "@/models/index.js";
import { publishInternalEvent } from "@/services/stream.js";
import { webhookEventTypes } from "@/models/entities/webhook.js"; import { webhookEventTypes } from "@/models/entities/webhook.js";
export const meta = { export const meta = {
@ -41,7 +40,7 @@ export default define(meta, paramDef, async (ps, user) => {
on: ps.on, on: ps.on,
}).then((x) => Webhooks.findOneByOrFail(x.identifiers[0])); }).then((x) => Webhooks.findOneByOrFail(x.identifiers[0]));
publishInternalEvent("webhookCreated", webhook); publishToInternalStream(InternalEvent.WebhookCreated, webhook);
return webhook; return webhook;
}); });

View file

@ -1,7 +1,7 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js"; import { ApiError } from "@/server/api/error.js";
import { Webhooks } from "@/models/index.js"; import { Webhooks } from "@/models/index.js";
import { publishInternalEvent } from "@/services/stream.js"; import { InternalEvent, publishToInternalStream } from "backend-rs";
export const meta = { export const meta = {
tags: ["webhooks"], tags: ["webhooks"],
@ -39,5 +39,5 @@ export default define(meta, paramDef, async (ps, user) => {
await Webhooks.delete(webhook.id); await Webhooks.delete(webhook.id);
publishInternalEvent("webhookDeleted", webhook); publishToInternalStream(InternalEvent.WebhookDeleted, webhook);
}); });

View file

@ -1,7 +1,7 @@
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js"; import { ApiError } from "@/server/api/error.js";
import { Webhooks } from "@/models/index.js"; import { Webhooks } from "@/models/index.js";
import { publishInternalEvent } from "@/services/stream.js"; import { InternalEvent, publishToInternalStream } from "backend-rs";
import { webhookEventTypes } from "@/models/entities/webhook.js"; import { webhookEventTypes } from "@/models/entities/webhook.js";
export const meta = { export const meta = {
@ -57,5 +57,5 @@ export default define(meta, paramDef, async (ps, user) => {
active: ps.active, active: ps.active,
}); });
publishInternalEvent("webhookUpdated", webhook); publishToInternalStream(InternalEvent.WebhookUpdated, webhook);
}); });

View file

@ -402,7 +402,7 @@ export const paramDef = {
} as const; } as const;
export default define(meta, paramDef, async (ps, me) => { export default define(meta, paramDef, async (ps, me) => {
const instance = await fetchMeta(); const instanceMeta = await fetchMeta();
const emojis = await Emojis.find({ const emojis = await Emojis.find({
where: { where: {
@ -425,56 +425,57 @@ export default define(meta, paramDef, async (ps, me) => {
}); });
const response: any = { const response: any = {
maintainerName: instance.maintainerName, maintainerName: instanceMeta.maintainerName,
maintainerEmail: instance.maintainerEmail, maintainerEmail: instanceMeta.maintainerEmail,
version: config.version, version: config.version,
name: instance.name, name: instanceMeta.name,
uri: config.url, uri: config.url,
description: instance.description, description: instanceMeta.description,
langs: instance.langs, langs: instanceMeta.langs,
tosUrl: instance.tosUrl, tosUrl: instanceMeta.tosUrl,
moreUrls: instance.moreUrls, moreUrls: instanceMeta.moreUrls,
repositoryUrl: instance.repositoryUrl, repositoryUrl: instanceMeta.repositoryUrl,
feedbackUrl: instance.feedbackUrl, feedbackUrl: instanceMeta.feedbackUrl,
secureMode: instance.secureMode, secureMode: instanceMeta.secureMode,
privateMode: instance.privateMode, privateMode: instanceMeta.privateMode,
disableRegistration: instance.disableRegistration, disableRegistration: instanceMeta.disableRegistration,
disableLocalTimeline: instance.disableLocalTimeline, disableLocalTimeline: instanceMeta.disableLocalTimeline,
disableRecommendedTimeline: instance.disableRecommendedTimeline, disableRecommendedTimeline: instanceMeta.disableRecommendedTimeline,
disableGlobalTimeline: instance.disableGlobalTimeline, disableGlobalTimeline: instanceMeta.disableGlobalTimeline,
enableGuestTimeline: instance.enableGuestTimeline, enableGuestTimeline: instanceMeta.enableGuestTimeline,
driveCapacityPerLocalUserMb: instance.localDriveCapacityMb, driveCapacityPerLocalUserMb: instanceMeta.localDriveCapacityMb,
driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb, driveCapacityPerRemoteUserMb: instanceMeta.remoteDriveCapacityMb,
antennaLimit: instance.antennaLimit, antennaLimit: instanceMeta.antennaLimit,
emailRequiredForSignup: instance.emailRequiredForSignup, emailRequiredForSignup: instanceMeta.emailRequiredForSignup,
enableHcaptcha: instance.enableHcaptcha, enableHcaptcha: instanceMeta.enableHcaptcha,
hcaptchaSiteKey: instance.hcaptchaSiteKey, hcaptchaSiteKey: instanceMeta.hcaptchaSiteKey,
enableRecaptcha: instance.enableRecaptcha, enableRecaptcha: instanceMeta.enableRecaptcha,
recaptchaSiteKey: instance.recaptchaSiteKey, recaptchaSiteKey: instanceMeta.recaptchaSiteKey,
swPublickey: instance.swPublicKey, swPublickey: instanceMeta.swPublicKey,
themeColor: instance.themeColor, themeColor: instanceMeta.themeColor,
mascotImageUrl: instance.mascotImageUrl, mascotImageUrl: instanceMeta.mascotImageUrl,
bannerUrl: instance.bannerUrl, bannerUrl: instanceMeta.bannerUrl,
errorImageUrl: instance.errorImageUrl, errorImageUrl: instanceMeta.errorImageUrl,
iconUrl: instance.iconUrl, iconUrl: instanceMeta.iconUrl,
backgroundImageUrl: instance.backgroundImageUrl, backgroundImageUrl: instanceMeta.backgroundImageUrl,
logoImageUrl: instance.logoImageUrl, logoImageUrl: instanceMeta.logoImageUrl,
maxNoteTextLength: config.maxNoteLength, // for backward compatibility maxNoteTextLength: config.maxNoteLength, // for backward compatibility
maxCaptionTextLength: config.maxCaptionLength, maxCaptionTextLength: config.maxCaptionLength,
emojis: instance.privateMode && !me ? [] : await Emojis.packMany(emojis), emojis:
instanceMeta.privateMode && !me ? [] : await Emojis.packMany(emojis),
// クライアントの手間を減らすためあらかじめJSONに変換しておく // クライアントの手間を減らすためあらかじめJSONに変換しておく
defaultLightTheme: instance.defaultLightTheme defaultLightTheme: instanceMeta.defaultLightTheme
? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) ? JSON.stringify(JSON5.parse(instanceMeta.defaultLightTheme))
: null, : null,
defaultDarkTheme: instance.defaultDarkTheme defaultDarkTheme: instanceMeta.defaultDarkTheme
? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) ? JSON.stringify(JSON5.parse(instanceMeta.defaultDarkTheme))
: null, : null,
ads: ads:
instance.privateMode && !me instanceMeta.privateMode && !me
? [] ? []
: ads.map((ad) => ({ : ads.map((ad) => ({
id: ad.id, id: ad.id,
@ -483,50 +484,52 @@ export default define(meta, paramDef, async (ps, me) => {
ratio: ad.ratio, ratio: ad.ratio,
imageUrl: ad.imageUrl, imageUrl: ad.imageUrl,
})), })),
enableEmail: instance.enableEmail, enableEmail: instanceMeta.enableEmail,
enableServiceWorker: instance.enableServiceWorker, enableServiceWorker: instanceMeta.enableServiceWorker,
translatorAvailable: translatorAvailable:
instance.deeplAuthKey != null || instance.libreTranslateApiUrl != null, instanceMeta.deeplAuthKey != null ||
defaultReaction: instance.defaultReaction, instanceMeta.libreTranslateApiUrl != null,
donationLink: instance.donationLink, defaultReaction: instanceMeta.defaultReaction,
enableServerMachineStats: instance.enableServerMachineStats, donationLink: instanceMeta.donationLink,
enableIdenticonGeneration: instance.enableIdenticonGeneration, enableServerMachineStats: instanceMeta.enableServerMachineStats,
enableIdenticonGeneration: instanceMeta.enableIdenticonGeneration,
...(ps.detail ...(ps.detail
? { ? {
pinnedPages: instance.privateMode && !me ? [] : instance.pinnedPages, pinnedPages:
instanceMeta.privateMode && !me ? [] : instanceMeta.pinnedPages,
pinnedClipId: pinnedClipId:
instance.privateMode && !me ? [] : instance.pinnedClipId, instanceMeta.privateMode && !me ? [] : instanceMeta.pinnedClipId,
cacheRemoteFiles: instance.cacheRemoteFiles, cacheRemoteFiles: instanceMeta.cacheRemoteFiles,
markLocalFilesNsfwByDefault: instance.markLocalFilesNsfwByDefault, markLocalFilesNsfwByDefault: instanceMeta.markLocalFilesNsfwByDefault,
requireSetup: (await countLocalUsers()) === 0, requireSetup: (await countLocalUsers()) === 0,
} }
: {}), : {}),
}; };
if (ps.detail) { if (ps.detail) {
if (!instance.privateMode || me) { if (!instanceMeta.privateMode || me) {
const proxyAccount = instance.proxyAccountId const proxyAccount = instanceMeta.proxyAccountId
? await Users.pack(instance.proxyAccountId).catch(() => null) ? await Users.pack(instanceMeta.proxyAccountId).catch(() => null)
: null; : null;
response.proxyAccountName = proxyAccount ? proxyAccount.username : null; response.proxyAccountName = proxyAccount ? proxyAccount.username : null;
} }
response.features = { response.features = {
registration: !instance.disableRegistration, registration: !instanceMeta.disableRegistration,
localTimeLine: !instance.disableLocalTimeline, localTimeLine: !instanceMeta.disableLocalTimeline,
recommendedTimeline: !instance.disableRecommendedTimeline, recommendedTimeline: !instanceMeta.disableRecommendedTimeline,
globalTimeLine: !instance.disableGlobalTimeline, globalTimeLine: !instanceMeta.disableGlobalTimeline,
guestTimeline: instance.enableGuestTimeline, guestTimeline: instanceMeta.enableGuestTimeline,
emailRequiredForSignup: instance.emailRequiredForSignup, emailRequiredForSignup: instanceMeta.emailRequiredForSignup,
hcaptcha: instance.enableHcaptcha, hcaptcha: instanceMeta.enableHcaptcha,
recaptcha: instance.enableRecaptcha, recaptcha: instanceMeta.enableRecaptcha,
objectStorage: instance.useObjectStorage, objectStorage: instanceMeta.useObjectStorage,
serviceWorker: instance.enableServiceWorker, serviceWorker: instanceMeta.enableServiceWorker,
postEditing: true, postEditing: true,
postImports: instance.experimentalFeatures?.postImports || false, postImports: instanceMeta.experimentalFeatures?.postImports || false,
miauth: true, miauth: true,
}; };
} }

Some files were not shown because too many files have changed in this diff Show more