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
.weblate
animated.svg
compose.yml
docker-compose.yml
docker-compose.example.yml
title.svg
@ -48,6 +49,7 @@ title.svg
/dev
/docs
/scripts
!/scripts/copy-index.mjs
!/scripts/copy-assets.mjs
biome.json
CODE_OF_CONDUCT.md

View file

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

View file

@ -22,17 +22,17 @@ bcrypt = { version = "0.15.1", default-features = false }
chrono = { version = "0.4.38", default-features = false }
convert_case = { version = "0.6.0", 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 }
image = { version = "0.25.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 }
pretty_assertions = { version = "1.4.0", default-features = false }
proc-macro2 = { version = "1.0.86", default-features = false }
quote = { version = "1.0.36", 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 }
rmp-serde = { version = "1.3.0", 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 }
sysinfo = { version = "0.30.13", 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 }
tracing = { version = "0.1.40", 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
FROM docker.io/node:20-alpine as build
# Install dev and compilation dependencies, build files
FROM docker.io/node:20-alpine AS build
WORKDIR /firefish
# Copy only backend-rs pnpm-related files first, to cache efficiently
COPY package.json pnpm-workspace.yaml ./
COPY packages/backend-rs/package.json packages/backend-rs/package.json
# Install compilation dependencies
# Install build tools and work around the linker name issue
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
ENV PATH="/root/.cargo/bin:${PATH}"
# Copy only backend-rs dependency-related files first, to cache efficiently
COPY packages/macro-rs packages/macro-rs/
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
RUN corepack enable && corepack prepare pnpm@latest --activate
# Configure pnpm, and install backend-rs dependencies
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
# Build
COPY . ./
# Build other workspaces
RUN NODE_ENV='production' NODE_OPTIONS='--max_old_space_size=3072' pnpm run --recursive --filter '!backend-rs' build && pnpm run build:assets
RUN pnpm install --frozen-lockfile
RUN cargo fetch --locked --manifest-path Cargo.toml
RUN NODE_ENV='production' NODE_OPTIONS='--max_old_space_size=3072' pnpm run build
# Trim down the dependencies to only those for production
RUN find . -path '*/node_modules/*' -delete && pnpm install --prod --frozen-lockfile
## Runtime container
# Runtime container
FROM docker.io/node:20-alpine
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/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/packages/backend/built /firefish/packages/backend/built
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.
## 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
- Added `i/export-followers` endpoint.

View file

@ -2,9 +2,16 @@
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.
- 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)
- Add followers list export feature

View file

@ -1,8 +1,12 @@
# 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.
## Runtime dependencies
### Runtime dependencies
- 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
@ -17,7 +21,7 @@ Firefish depends on the following software.
- [KeyDB](https://keydb.dev/)
- Another [Redis](https://redis.io/) / [Valkey](https://valkey.io/) server
## Build dependencies
### Build dependencies
- At least [Rust](https://www.rust-lang.org/) v1.74
- 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>
<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:
@ -44,7 +48,7 @@ You can install Firefish on FreeBSD by adding these extra steps to the standard
```json
"pnpm": {
"overrides": {
"rollup": "npm:@rollup/wasm-node@4.17.2"
"rollup": "npm:@rollup/wasm-node
}
}
```

View file

@ -1,6 +1,6 @@
{
"name": "firefish",
"version": "20240725",
"version": "20240728",
"repository": {
"type": "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 type Activity = 'Follow';
export interface Ad {
id: string
createdAt: DateTimeWithTimeZone
@ -217,7 +219,7 @@ export interface Config {
proxySmtp?: string
proxyBypassHosts?: Array<string>
allowedPrivateNetworks?: Array<string>
maxFileSize?: number
maxFileSize: number
accessLog?: string
clusterLimits: WorkerConfig
cuid?: IdConfig
@ -369,6 +371,40 @@ export interface Emoji {
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 fetchMeta(): Promise<Meta>
@ -389,6 +425,13 @@ export interface Following {
followeeSharedInbox: string | null
}
export interface FollowRelay {
id: string
type: Activity
actor: string
object: string
}
export interface FollowRequest {
id: string
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 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 getRelayActorId(): Promise<string>
export declare function getTimestamp(id: string): number
/** Prints the greeting message and the Firefish version to stdout. */
@ -529,8 +574,20 @@ export interface Instance {
faviconUrl: string | null
}
export type InternalActor = 'instance'|
'relay';
export declare enum InternalEvent {
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.
@ -832,6 +889,15 @@ export interface NoteEdit {
emojis: Array<string>
}
export declare enum NoteEvent {
Delete = 0,
React = 1,
Unreact = 2,
Reply = 3,
Update = 4,
Vote = 5
}
export interface NoteFavorite {
id: string
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 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 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 {
img: string | null
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 */
export declare function removeOldAttestationChallenges(): Promise<void>
export declare function renderFollowRelay(relayId: string): Promise<FollowRelay>
export interface RenoteMuting {
id: string
createdAt: DateTimeWithTimeZone
@ -1406,6 +1484,17 @@ export type UserEmojiModPerm = 'add'|
'mod'|
'unauthorized';
export declare enum UserEvent {
Disconnect = 0,
FollowChannel = 1,
UnfollowChannel = 2,
UpdateProfile = 3,
Mute = 4,
Unmute = 5,
Follow = 6,
Unfollow = 7
}
export interface UserGroup {
id: string
createdAt: DateTimeWithTimeZone

View file

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

View file

@ -200,7 +200,7 @@ pub struct Config {
pub proxy_smtp: Option<String>,
pub proxy_bypass_hosts: 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 cluster_limits: WorkerConfig,
pub cuid: Option<IdConfig>,
@ -309,7 +309,7 @@ pub fn load_config() -> Config {
proxy_smtp: server_config.proxy_smtp,
proxy_bypass_hosts: server_config.proxy_bypass_hosts,
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,
cluster_limits,
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;
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
pub mod acct;
pub mod activitypub;
pub mod internal_actor;
pub mod nodeinfo;

View file

@ -5,8 +5,13 @@ pub mod chat_index;
pub mod custom_emoji;
pub mod drive;
pub mod group_chat;
pub mod internal;
pub mod main;
pub mod moderation;
pub mod note;
pub mod note_edit;
pub mod notes;
pub mod user;
use crate::{
config::CONFIG,
@ -30,9 +35,7 @@ pub enum Stream {
note_id: String,
},
Notes,
UserList {
list_id: String,
},
NoteEdit,
Main {
user_id: String,
},
@ -86,8 +89,8 @@ pub async fn publish_to_stream(
Stream::User { user_id } => format!("user:{user_id}"),
Stream::Channel { channel_id } => format!("channelStream:{channel_id}"),
Stream::Note { note_id } => format!("noteStream:{note_id}"),
Stream::NoteEdit => "noteUpdatesStream".to_owned(),
Stream::Notes => "notesStream".to_owned(),
Stream::UserList { list_id } => format!("userListStream:{list_id}"),
Stream::Main { user_id } => format!("mainStream:{user_id}"),
Stream::Drive { user_id } => format!("driveStream:{user_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",
"async-lock": "1.4.1",
"async-mutex": "0.5.0",
"aws-sdk": "2.1662.0",
"aws-sdk": "2.1664.0",
"axios": "1.7.2",
"backend-rs": "workspace:*",
"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 MAX_NOTE_TEXT_LENGTH = Math.min(
config.maxNoteLength ?? 3000,
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));
});
const meta = await fetchMeta();
if (!meta.enableServerMachineStats) return;
const instanceMeta = await fetchMeta();
if (!instanceMeta.enableServerMachineStats) return;
async function tick() {
const stats = {

View file

@ -1,9 +1,29 @@
import type { MigrationInterface, QueryRunner } from "typeorm";
import { v4 as uuid } from "uuid";
import { genRsaKeyPair } from "@/misc/gen-key-pair.js";
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) {
const password = uuid();

View file

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

View file

@ -3,9 +3,9 @@ import type { ILocalUser } from "@/models/entities/user.js";
import { Users } from "@/models/index.js";
export async function fetchProxyAccount(): Promise<ILocalUser | null> {
const meta = await fetchMeta();
if (meta.proxyAccountId == null) return null;
const instanceMeta = await fetchMeta();
if (instanceMeta.proxyAccountId == null) return null;
return (await Users.findOneByOrFail({
id: meta.proxyAccountId,
id: instanceMeta.proxyAccountId,
})) 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}/avatar`)) return url;
const meta = await fetchMeta();
const baseUrl = meta
? meta.objectStorageBaseUrl ??
`${meta.objectStorageUseSsl ? "https" : "http"}://${
meta.objectStorageEndpoint
}${meta.objectStoragePort ? `:${meta.objectStoragePort}` : ""}/${
meta.objectStorageBucket
const instanceMeta = await fetchMeta();
const baseUrl = instanceMeta
? instanceMeta.objectStorageBaseUrl ??
`${instanceMeta.objectStorageUseSsl ? "https" : "http"}://${
instanceMeta.objectStorageEndpoint
}${instanceMeta.objectStoragePort ? `:${instanceMeta.objectStoragePort}` : ""}/${
instanceMeta.objectStorageBucket
}`
: null;
if (baseUrl !== null && url.startsWith(baseUrl)) return url;

View file

@ -155,13 +155,13 @@ webhookDeliverQueue
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 (to == null) return null;
const data = {
user: {
id: user.id,
id: userId,
},
content,
to,

View file

@ -1,4 +1,3 @@
import * as Post from "@/misc/post.js";
import create from "@/services/note/create.js";
import { NoteFiles, Users } from "@/models/index.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 type { Note } from "@/models/entities/note.js";
import { genId } from "backend-rs";
import { noteVisibilities } from "@/types.js";
const logger = queueLogger.createSubLogger("import-firefish-post");
@ -52,10 +52,15 @@ export async function importCkPost(
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({
createdAt: createdAt,
text: text,
createdAt,
text: post.text || undefined,
userId: user.id,
});
@ -95,16 +100,16 @@ export async function importCkPost(
note = await create(
user,
{
createdAt: createdAt,
createdAt,
scheduledAt: undefined,
files: files.length === 0 ? undefined : files,
poll: undefined,
text: text || undefined,
text: post.text || undefined,
reply: post.replyId ? job.data.parent : null,
renote: post.renoteId ? job.data.parent : null,
cw: cw,
localOnly,
visibility: visibility,
cw: post.cw,
localOnly: post.localOnly,
visibility,
visibleUsers: [],
channel: null,
apMentions: new Array(0),

View file

@ -2,7 +2,7 @@ import type Bull from "bull";
import { In } from "typeorm";
import { Mutings } from "@/models/index.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");
@ -23,9 +23,11 @@ export async function checkExpiredMutings(
id: In(expired.map((m) => m.id)),
});
for (const m of expired) {
publishUserEvent(m.muterId, "unmute", m.mutee!);
}
await Promise.all(
expired.map((m) =>
publishToUserStream(m.muterId, UserEvent.Unmute, m.mutee),
),
);
}
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";
export async function hasSignature(req: IncomingMessage): Promise<string> {
const meta = await fetchMeta();
const required = meta.secureMode || meta.privateMode;
const instanceMeta = await fetchMeta();
const required = instanceMeta.secureMode || instanceMeta.privateMode;
try {
httpSignature.parseRequest(req, { headers: [] });
@ -30,8 +30,8 @@ export async function hasSignature(req: IncomingMessage): Promise<string> {
}
export async function checkFetch(req: IncomingMessage): Promise<number> {
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
const instanceMeta = await fetchMeta();
if (instanceMeta.secureMode || instanceMeta.privateMode) {
if (req.headers.host !== config.host) return 400;
let signature;

View file

@ -148,7 +148,7 @@ export default class DeliverManager {
// skip instances as indicated
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}`);
const instance = await fetchMeta();
const instanceMeta = await fetchMeta();
let file = await uploadFromUrl({
url: image.url,
user: actor,
uri: image.url,
sensitive: image.sensitive,
isLink: !instance.cacheRemoteFiles,
isLink: !instanceMeta.cacheRemoteFiles,
comment: truncate(image.name, config.maxCaptionLength),
usageHint: usage,
});

View file

@ -15,11 +15,13 @@ import { apLogger } from "../logger.js";
import type { DriveFile } from "@/models/entities/drive-file.js";
import {
type ImageSize,
NoteEvent,
extractHost,
genId,
getImageSizeFromUrl,
isBlockedServer,
isSameOrigin,
publishToNoteStream,
toPuny,
} from "backend-rs";
import {
@ -47,7 +49,6 @@ import { parseAudience } from "../audience.js";
import { extractApMentions } from "./mention.js";
import DbResolver from "../db-resolver.js";
import { StatusError } from "@/misc/fetch.js";
import { publishNoteStream } from "@/services/stream.js";
import { extractHashtags } from "@/misc/extract-hashtags.js";
import { UserProfiles } from "@/models/index.js";
import { In } from "typeorm";
@ -795,7 +796,7 @@ export async function updateNote(value: string | IObject, resolver?: Resolver) {
if (publishing) {
// Publish update event for the updated note details
publishNoteStream(note.id, "updated", {
publishToNoteStream(note.id, NoteEvent.Update, {
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 type { Emoji } from "@/models/entities/emoji.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 { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.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 { StatusError } from "@/misc/fetch.js";
import { uriPersonCache } from "@/services/user-cache.js";
import { publishInternalEvent } from "@/services/stream.js";
import { db } from "@/db/postgre.js";
import { apLogger } from "../logger.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
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 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;
const id =
typeof object.id === "string" && object.id.startsWith(config.url)
@ -11,7 +10,7 @@ export default (object: any, user: { id: User["id"] }) => {
return {
type: "Undo",
...(id ? { id } : {}),
actor: `${config.url}/users/${user.id}`,
actor: `${config.url}/users/${userId}`,
object,
published: new Date().toISOString(),
};

View file

@ -2,7 +2,7 @@ import { config } from "@/config.js";
import type { ILocalUser } from "@/models/entities/user.js";
import {
extractHost,
getInternalActor,
getInstanceActor,
isAllowedServer,
isBlockedServer,
isSelfHost,
@ -112,7 +112,7 @@ export default class Resolver {
}
if (!this.user) {
this.user = await getInternalActor("instance");
this.user = (await getInstanceActor()) as ILocalUser;
}
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 renderEmoji from "@/remote/activitypub/renderer/emoji.js";
import { inbox as processInbox } from "@/queue/index.js";
import { fetchMeta, getInternalActor, isSelfHost } from "backend-rs";
import { fetchMeta, getInstanceActor, isSelfHost } from "backend-rs";
import {
Notes,
Users,
@ -242,8 +242,8 @@ router.get("/notes/:note", async (ctx, next) => {
ctx.body = renderActivity(await renderNote(note, false));
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
const instanceMeta = await fetchMeta();
if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
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));
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
const instanceMeta = await fetchMeta();
if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
ctx.set("Cache-Control", "public, max-age=180");
@ -295,7 +295,7 @@ router.get("/users/:user/collections/featured", Featured);
// publickey
router.get("/users/:user/publickey", async (ctx) => {
const instanceActor = await getInternalActor("instance");
const instanceActor = (await getInstanceActor()) as ILocalUser;
if (ctx.params.user === instanceActor.id) {
ctx.body = renderActivity(
renderKey(instanceActor, await getUserKeypair(instanceActor.id)),
@ -327,8 +327,8 @@ router.get("/users/:user/publickey", async (ctx) => {
if (Users.isLocalUser(user)) {
ctx.body = renderActivity(renderKey(user, keypair));
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
const instanceMeta = await fetchMeta();
if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
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));
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
const instanceMeta = await fetchMeta();
if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
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) => {
if (!isActivityPubReq(ctx)) return await next();
const instanceActor = await getInternalActor("instance");
const instanceActor = (await getInstanceActor()) as ILocalUser;
if (ctx.params.user === instanceActor.id) {
await userInfo(ctx, instanceActor);
return;
@ -386,7 +386,7 @@ router.get("/@:user", async (ctx, next) => {
if (!isActivityPubReq(ctx)) return await next();
if (ctx.params.user === "instance.actor") {
const instanceActor = await getInternalActor("instance");
const instanceActor = (await getInstanceActor()) as ILocalUser;
await userInfo(ctx, instanceActor);
return;
}
@ -407,7 +407,7 @@ router.get("/@:user", async (ctx, next) => {
});
router.get("/actor", async (ctx, _next) => {
const instanceActor = await getInternalActor("instance");
const instanceActor = (await getInstanceActor()) as ILocalUser;
await userInfo(ctx, instanceActor);
});
//#endregion
@ -431,8 +431,8 @@ router.get("/emojis/:emoji", async (ctx) => {
}
ctx.body = renderActivity(renderEmoji(emoji));
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
const instanceMeta = await fetchMeta();
if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
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));
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
const instanceMeta = await fetchMeta();
if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
ctx.set("Cache-Control", "public, max-age=180");
@ -501,8 +501,8 @@ router.get(
}
ctx.body = renderActivity(renderFollow(follower, followee));
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
const instanceMeta = await fetchMeta();
if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
ctx.set("Cache-Control", "public, max-age=180");
@ -544,8 +544,8 @@ router.get("/follows/:followRequestId", async (ctx: Router.RouterContext) => {
return;
}
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
const instanceMeta = await fetchMeta();
if (instanceMeta.secureMode || instanceMeta.privateMode) {
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
} else {
ctx.set("Cache-Control", "public, max-age=180");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,9 +1,10 @@
import { publishMainStream } from "@/services/stream.js";
import {
publishToChatStream,
publishToGroupChatStream,
publishToChatIndexStream,
sendPushNotification,
publishToMainStream,
Event,
} from "backend-rs";
import type { User, IRemoteUser } from "@/models/entities/user.js";
import type { MessagingMessage } from "@/models/entities/messaging-message.js";
@ -61,7 +62,7 @@ export async function readUserMessagingMessage(
if (!(await Users.getHasUnreadMessagingMessage(userId))) {
// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
publishMainStream(userId, "readAllMessagingMessages");
await publishToMainStream(userId, Event.ReadAllChats, {});
await sendPushNotification(userId, "readAllChats", {});
} else {
// そのユーザーとのメッセージで未読がなければイベント発行
@ -135,7 +136,7 @@ export async function readGroupMessagingMessage(
if (!(await Users.getHasUnreadMessagingMessage(userId))) {
// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
publishMainStream(userId, "readAllMessagingMessages");
await publishToMainStream(userId, Event.ReadAllChats, {});
await sendPushNotification(userId, "readAllChats", {});
} else {
// そのグループにおいて未読がなければイベント発行
@ -173,10 +174,10 @@ export async function deliverReadActivity(
undefined,
contents,
);
deliver(user, renderActivity(collection), recipient.inbox);
deliver(user.id, renderActivity(collection), recipient.inbox);
} else {
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 { publishMainStream } from "@/services/stream.js";
import { sendPushNotification } from "backend-rs";
import { Event, publishToMainStream, sendPushNotification } from "backend-rs";
import type { User } from "@/models/entities/user.js";
import type { Notification } from "@/models/entities/notification.js";
import { Notifications, Users } from "@/models/index.js";
@ -46,7 +45,7 @@ export async function readNotificationByQuery(
}
function postReadAllNotifications(userId: User["id"]) {
publishMainStream(userId, "readAllNotifications");
publishToMainStream(userId, Event.ReadAllNotifications, {});
return sendPushNotification(userId, "readAllNotifications", {});
}
@ -54,7 +53,7 @@ function postReadNotifications(
userId: User["id"],
notificationIds: Notification["id"][],
) {
publishMainStream(userId, "readNotifications", notificationIds);
publishToMainStream(userId, Event.ReadNotifications, notificationIds);
return sendPushNotification(userId, "readNotifications", {
notificationIds,
});

View file

@ -3,8 +3,7 @@ import type Koa from "koa";
import { config } from "@/config.js";
import type { ILocalUser } from "@/models/entities/user.js";
import { Signins } from "@/models/index.js";
import { genIdAt } from "backend-rs";
import { publishMainStream } from "@/services/stream.js";
import { Event, genIdAt, publishToMainStream } from "backend-rs";
export default function (ctx: Koa.Context, user: ILocalUser, redirect = false) {
if (redirect) {
@ -40,6 +39,6 @@ export default function (ctx: Koa.Context, user: ILocalUser, redirect = false) {
}).then((x) => Signins.findOneByOrFail(x.identifiers[0]));
// 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 { Users } from "@/models/index.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";
export const meta = {
@ -53,6 +53,6 @@ export default define(meta, paramDef, async (ps, me) => {
if (Users.isLocalUser(user)) {
// 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 { publishMainStream } from "@/services/stream.js";
import { Event, publishToMainStream } from "backend-rs";
import define from "@/server/api/define.js";
export const meta = {
@ -36,5 +36,5 @@ export default define(meta, paramDef, async (ps) => {
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 { publishMainStream } from "@/services/stream.js";
import { Event, publishToMainStream } from "backend-rs";
import define from "@/server/api/define.js";
export const meta = {
@ -38,5 +38,5 @@ export default define(meta, paramDef, async (ps) => {
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 { Users } from "@/models/index.js";
import { insertModerationLog } from "@/services/insert-moderation-log.js";
import { publishInternalEvent } from "@/services/stream.js";
import { InternalEvent, publishToInternalStream } from "backend-rs";
export const meta = {
tags: ["admin"],
@ -33,7 +33,7 @@ export default define(meta, paramDef, async (ps, me) => {
driveCapacityOverrideMb: ps.overrideMb,
});
publishInternalEvent("localUserUpdated", {
publishToInternalStream(InternalEvent.LocalUser, {
id: user.id,
});

View file

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

View file

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

View file

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

View file

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

View file

@ -1,9 +1,10 @@
import define from "@/server/api/define.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 { renderActivity } from "@/remote/activitypub/renderer/index.js";
import { renderFlag } from "@/remote/activitypub/renderer/flag.js";
import { ILocalUser } from "@/models/entities/user";
export const meta = {
tags: ["admin"],
@ -29,11 +30,11 @@ export default define(meta, paramDef, async (ps, me) => {
}
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 });
deliver(
actor,
actor.id,
renderActivity(renderFlag(actor, [targetUser.uri!], report.comment)),
targetUser.inbox,
);

View file

@ -1,6 +1,5 @@
import define from "@/server/api/define.js";
import { Users } from "@/models/index.js";
import { publishInternalEvent } from "@/services/stream.js";
import type { EmojiModPerm } from "@/models/entities/user.js";
import { unsafeCast } from "@/prelude/unsafe-cast.js";
@ -36,9 +35,4 @@ export default define(meta, paramDef, async (ps) => {
await Users.update(user.id, {
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 { Users } from "@/models/index.js";
import { insertModerationLog } from "@/services/insert-moderation-log.js";
import { publishInternalEvent } from "@/services/stream.js";
import { InternalEvent, publishToInternalStream } from "backend-rs";
export const meta = {
tags: ["admin"],
@ -33,7 +33,7 @@ export default define(meta, paramDef, async (ps, me) => {
isSilenced: true,
});
publishInternalEvent("userChangeSilencedState", {
publishToInternalStream(InternalEvent.Silence, {
id: user.id,
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 { insertModerationLog } from "@/services/insert-moderation-log.js";
import { doPostSuspend } from "@/services/suspend-user.js";
import { publishUserEvent } from "@/services/stream.js";
import { publishToUserStream, UserEvent } from "backend-rs";
export const meta = {
tags: ["admin"],
@ -46,7 +46,7 @@ export default define(meta, paramDef, async (ps, me) => {
// Terminate streaming
if (Users.isLocalUser(user)) {
publishUserEvent(user.id, "terminate", {});
await publishToUserStream(user.id, UserEvent.Disconnect, {});
}
(async () => {

View file

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

View file

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

View file

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

View file

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

View file

@ -1,8 +1,7 @@
import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js";
import { Channels, ChannelFollowings } from "@/models/index.js";
import { genIdAt } from "backend-rs";
import { publishUserEvent } from "@/services/stream.js";
import { genIdAt, publishToUserStream, UserEvent } from "backend-rs";
export const meta = {
tags: ["channels"],
@ -46,5 +45,5 @@ export default define(meta, paramDef, async (ps, user) => {
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 { ApiError } from "@/server/api/error.js";
import { Channels, ChannelFollowings } from "@/models/index.js";
import { publishUserEvent } from "@/services/stream.js";
import { publishToUserStream, UserEvent } from "backend-rs";
export const meta = {
tags: ["channels"],
@ -41,5 +41,5 @@ export default define(meta, paramDef, async (ps, user) => {
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;
export default define(meta, paramDef, async () => {
const meta = await fetchMeta();
const motd = await Promise.all(meta.customMotd.map((x) => x));
const instanceMeta = await fetchMeta();
const motd = await Promise.all(instanceMeta.customMotd.map((x) => x));
return motd;
});

View file

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

View file

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

View file

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

View file

@ -37,15 +37,13 @@ export const paramDef = {
required: ["name"],
} as const;
export default define(meta, paramDef, async (ps, me) => {
const emoji = await Emojis.findOne({
where: {
export default define(meta, paramDef, async (ps) => {
const emoji = await Emojis.findOneBy({
name: ps.name,
host: IsNull(),
},
});
if (!emoji) {
if (emoji == null) {
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") {
const meta = await fetchMeta();
const instanceMeta = await fetchMeta();
if (ps.blocked) {
if (meta.blockedHosts.length === 0) {
if (instanceMeta.blockedHosts.length === 0) {
return [];
}
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)", {
blocks: meta.blockedHosts,
blocks: instanceMeta.blockedHosts,
});
}
}
if (typeof ps.silenced === "boolean") {
const meta = await fetchMeta();
const instanceMeta = await fetchMeta();
if (ps.silenced) {
if (meta.silencedHosts.length === 0) {
if (instanceMeta.silencedHosts.length === 0) {
return [];
}
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)", {
silences: meta.silencedHosts,
silences: instanceMeta.silencedHosts,
});
}
}

View file

@ -66,8 +66,8 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async () => {
const instance = await fetchMeta();
const hiddenTags = instance.hiddenTags.map((t) => normalizeForSearch(t));
const instanceMeta = await fetchMeta();
const hiddenTags = instanceMeta.hiddenTags.map((t) => normalizeForSearch(t));
const now = new Date(); // 5分単位で丸めた現在日時
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 define from "@/server/api/define.js";
import { Users, UserProfiles } from "@/models/index.js";
@ -47,5 +47,5 @@ export default define(meta, paramDef, async (ps, user) => {
includeSecrets: true,
});
publishMainStream(user.id, "meUpdated", iObj);
publishToMainStream(user.id, Event.Me, iObj);
});

View file

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

View file

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

View file

@ -1,7 +1,6 @@
import { publishMainStream } from "@/services/stream.js";
import define from "@/server/api/define.js";
import { Users, UserProfiles } from "@/models/index.js";
import { verifyPassword } from "backend-rs";
import { Event, publishToMainStream, verifyPassword } from "backend-rs";
export const meta = {
requireCredential: true,
@ -38,5 +37,5 @@ export default define(meta, paramDef, async (ps, user) => {
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 { Users, UserSecurityKeys } from "@/models/index.js";
import { ApiError } from "@/server/api/error.js";
import { Event, publishToMainStream } from "backend-rs";
export const meta = {
requireCredential: true,
@ -54,5 +54,5 @@ export default define(meta, paramDef, async (ps, user) => {
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 acceptAllFollowRequests from "@/services/following/requests/accept-all.js";
import { publishToFollowers } from "@/services/i/update.js";
import { publishMainStream } from "@/services/stream.js";
import { stringToAcct } from "backend-rs";
import { Event, publishToMainStream, stringToAcct } from "backend-rs";
import { DAY } from "@/const.js";
import { apiLogger } from "@/server/api/logger.js";
import define from "@/server/api/define.js";
@ -97,7 +96,7 @@ export default define(meta, paramDef, async (ps, user) => {
});
// Publish meUpdated event
publishMainStream(user.id, "meUpdated", iObj);
publishToMainStream(user.id, Event.Me, iObj);
if (user.isLocked === false) {
acceptAllFollowRequests(user);

View file

@ -1,6 +1,6 @@
import type { User } from "@/models/entities/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 DeliverManager from "@/remote/activitypub/deliver-manager.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 { Followings, Users } from "@/models/index.js";
import { config } from "@/config.js";
import { publishMainStream } from "@/services/stream.js";
import { inspect } from "node:util";
export const meta = {
@ -134,7 +133,7 @@ export default define(meta, paramDef, async (ps, user) => {
dm.execute();
// Publish meUpdated event
publishMainStream(user.id, "meUpdated", iObj);
publishToMainStream(user.id, Event.Me, iObj);
const followings = await Followings.findBy({
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 { MessagingMessages, UserGroupJoinings } from "@/models/index.js";
@ -16,7 +16,7 @@ export const paramDef = {
required: [],
} as const;
export default define(meta, paramDef, async (ps, user) => {
export default define(meta, paramDef, async (_ps, user) => {
// Update documents
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 { NoteUnreads } from "@/models/index.js";
@ -23,6 +23,6 @@ export default define(meta, paramDef, async (ps, user) => {
});
// 全て既読になったイベントを発行
publishMainStream(user.id, "readAllUnreadMentions");
publishMainStream(user.id, "readAllUnreadSpecifiedNotes");
publishToMainStream(user.id, Event.ReadAllMentions, {});
publishToMainStream(user.id, Event.ReadAllDms, {});
});

View file

@ -1,8 +1,7 @@
import define from "@/server/api/define.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 { publishMainStream } from "@/services/stream.js";
export const meta = {
tags: ["account"],
@ -59,6 +58,6 @@ export default define(meta, paramDef, async (ps, user) => {
});
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 { 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 = {
requireCredential: true,
@ -41,15 +45,15 @@ export default define(meta, paramDef, async (ps, user) => {
});
// Publish event
publishInternalEvent("userTokenRegenerated", {
publishToInternalStream(InternalEvent.Token, {
id: user.id,
oldToken,
newToken,
});
publishMainStream(user.id, "myTokenRegenerated");
publishToMainStream(user.id, Event.RegenerateMyToken, {});
// Terminate streaming
setTimeout(() => {
publishUserEvent(user.id, "terminate", {});
publishToUserStream(user.id, UserEvent.Disconnect, {});
}, 5000);
});

View file

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

View file

@ -1,6 +1,6 @@
import define from "@/server/api/define.js";
import { AccessTokens } from "@/models/index.js";
import { publishUserEvent } from "@/services/stream.js";
import { publishToUserStream, UserEvent } from "backend-rs";
export const meta = {
requireCredential: true,
@ -26,6 +26,6 @@ export default define(meta, paramDef, async (ps, user) => {
});
// 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 rndstr from "rndstr";
import { config } from "@/config.js";
@ -6,7 +5,7 @@ import { Users, UserProfiles } from "@/models/index.js";
import { sendEmail } from "@/services/send-email.js";
import { ApiError } from "@/server/api/error.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";
export const meta = {
@ -72,7 +71,7 @@ export default define(meta, paramDef, async (ps, user) => {
});
// Publish meUpdated event
publishMainStream(user.id, "meUpdated", iObj);
publishToMainStream(user.id, Event.Me, iObj);
if (ps.email != null) {
const code = rndstr("a-z0-9", 16);

View file

@ -1,5 +1,10 @@
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 { publishToFollowers } from "@/services/i/update.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
publishMainStream(user.id, "meUpdated", iObj);
publishUserEvent(
await publishToMainStream(user.id, Event.Me, iObj);
await publishToUserStream(
user.id,
"updateUserProfile",
UserEvent.UpdateProfile,
await UserProfiles.findOneBy({ userId: user.id }),
);

View file

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

View file

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

View file

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

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