Compare commits

..

34 Commits

Author SHA1 Message Date
izzy
25025da70a Merge branch 'main' into 3-otel 2026-04-01 21:14:44 +01:00
Tom
f30b729ca9 fix: don't send self dm notifications (#706)
* fix: don't send self dm notifications

Signed-off-by: IAmTomahawkx <iamtomahawkx@gmail.com>

* fix: test failure due to wrong assertion (#707)

Signed-off-by: IAmTomahawkx <iamtomahawkx@gmail.com>

* fix: RA auto import moment

Signed-off-by: IAmTomahawkx <iamtomahawkx@gmail.com>

---------

Signed-off-by: IAmTomahawkx <iamtomahawkx@gmail.com>
2026-03-31 15:25:36 -07:00
Tom
f81e3291bd fix: test failure due to wrong assertion (#707) 2026-03-31 11:06:39 +01:00
Tom
1e80916b65 Fix/release please fix 4 (#703)
* fix: release-please-3

* fix: release please bs v4
2026-03-28 23:05:01 -07:00
Tom
8814bf4c23 fix: release-please-3 (#702) 2026-03-28 22:53:12 -07:00
Tom
5466ada9ae Fix: release please bs 2 electric boogaloo (#701)
* fix: update release (me from this) please

* fix: add dep to release please

* Revert "fix: update release (me from this) please"

This reverts commit 0618c492c7.
2026-03-28 22:42:13 -07:00
Tom
f0e513ccae fix: update release (me from this) please (#699) 2026-03-28 22:35:51 -07:00
stoat-release[bot]
4d4b0dd864 chore(main): release 0.12.0 (#602)
Co-authored-by: stoat-release[bot] <245062572+stoat-release[bot]@users.noreply.github.com>
2026-03-28 22:22:37 -07:00
izzy
d1631f6262 test: correct image size of extra bytes jpeg test
Signed-off-by: izzy <me@insrt.uk>
2026-01-25 20:22:47 +00:00
izzy
94aaeed758 chore: remove un-used import
Signed-off-by: izzy <me@insrt.uk>
2026-01-25 19:49:36 +00:00
izzy
945b7a6ecb Merge branch '2-refactor-core-files' into 3-otel 2026-01-25 19:49:08 +00:00
izzy
6418da51cd Merge branch 'main' into 2-refactor-core-files
# Conflicts:
#	Cargo.lock
2026-01-25 19:49:08 +00:00
izzy
74e36c7c2a Merge branch '2-refactor-core-files' into 3-otel 2026-01-24 23:44:18 +00:00
izzy
239112f935 Merge branch '1-mise-git-town' into 2-refactor-core-files 2026-01-24 23:44:17 +00:00
izzy
5968514231 Merge branch 'main' into 1-mise-git-town 2026-01-24 23:44:16 +00:00
izzy
0c3f76a406 Merge branch '2-refactor-core-files' into 3-otel 2026-01-24 23:36:45 +00:00
izzy
f988b09d9a Merge branch '1-mise-git-town' into 2-refactor-core-files 2026-01-24 23:36:44 +00:00
izzy
facaf3869f Merge branch 'main' into 1-mise-git-town 2026-01-24 23:36:42 +00:00
izzy
c96f89aaa9 Merge branch '2-refactor-core-files' into 3-otel 2026-01-24 23:35:50 +00:00
izzy
03d790007d Merge branch '1-mise-git-town' into 2-refactor-core-files 2026-01-24 23:35:49 +00:00
izzy
270a9ac625 Merge branch 'main' into 1-mise-git-town 2026-01-24 23:35:48 +00:00
izzy
6acc7a3e46 Merge branch '2-refactor-core-files' into 3-otel 2026-01-24 02:18:54 +00:00
izzy
e6a2571303 Merge branch '1-mise-git-town' into 2-refactor-core-files 2026-01-24 02:18:53 +00:00
izzy
ef36df5da1 Merge branch 'main' into 1-mise-git-town 2026-01-24 02:18:51 +00:00
izzy
d552442b43 Merge branch '2-refactor-core-files' into 3-otel 2026-01-24 02:11:10 +00:00
izzy
0fb4981556 Merge branch '1-mise-git-town' into 2-refactor-core-files 2026-01-24 02:11:09 +00:00
izzy
6b00c5aa42 Merge branch 'main' into 1-mise-git-town 2026-01-24 02:11:08 +00:00
izzy
503885d8dc Merge branch '1-mise-git-town' into 2-refactor-core-files 2026-01-24 02:02:55 +00:00
izzy
e896bec16a Merge branch 'main' into 1-mise-git-town 2026-01-24 02:02:54 +00:00
izzy
7468ba5b8a trigger workflow
Signed-off-by: izzy <me@insrt.uk>
2026-01-24 02:01:30 +00:00
izzy
d9ca1b8805 trigger workflow 2026-01-24 01:57:27 +00:00
izzy
97511059a4 feat: add otel logging, integrate with tracing 2026-01-24 01:27:32 +00:00
izzy
e896582a5c feat: repository architecture for files crate w. added tests
Signed-off-by: izzy <me@insrt.uk>
2026-01-24 01:25:42 +00:00
izzy
9c2ae59ecf chore: add git-town tool
Signed-off-by: izzy <me@insrt.uk>
2026-01-24 01:21:33 +00:00
41 changed files with 415 additions and 214 deletions

View File

@@ -2,4 +2,4 @@
#MISE description="Start Docker containers"
set -e
docker compose up -d
docker compose up -d "$@"

View File

@@ -2,4 +2,4 @@
#MISE description="Stop Docker containers"
set -e
docker compose down
docker compose down "$@"

View File

@@ -1,3 +1,3 @@
{
".": "0.11.5"
".": "0.12.0"
}

View File

@@ -1,5 +1,42 @@
# Changelog
## [0.12.0](https://github.com/stoatchat/stoatchat/compare/v0.11.5...v0.12.0) (2026-03-28)
### Features
* add bug report template for issue tracking ([#627](https://github.com/stoatchat/stoatchat/issues/627)) ([f777e28](https://github.com/stoatchat/stoatchat/commit/f777e2863c6ca50057c8b5d0a5be14915d287724))
* Add slowmode functionality to text channels ([#680](https://github.com/stoatchat/stoatchat/issues/680)) ([6107f24](https://github.com/stoatchat/stoatchat/commit/6107f242fd3ebaff71a15f9a16330ffbcb4f2d7b))
* Allow restricting server creation to specific users ([#685](https://github.com/stoatchat/stoatchat/issues/685)) ([edfa97d](https://github.com/stoatchat/stoatchat/commit/edfa97db108c9c81828547f98a1db5315cb5ba4a))
* compute thumbhash for images ([#596](https://github.com/stoatchat/stoatchat/issues/596)) ([c2d4369](https://github.com/stoatchat/stoatchat/commit/c2d4369e160f32d79bce0a0b0f14677f89de3669))
* Detect animation in image files for fetch_preview ([#574](https://github.com/stoatchat/stoatchat/issues/574)) ([3fa0abf](https://github.com/stoatchat/stoatchat/commit/3fa0abf47f5f42ddd8ee041fe4c44fbc5ba800c1))
* expose global and user limits in root API response ([#644](https://github.com/stoatchat/stoatchat/issues/644)) ([0b522eb](https://github.com/stoatchat/stoatchat/commit/0b522ebddc17f2e3f792ff5e2347793e9849fa23))
* implement time based message sweep on user ban ([#670](https://github.com/stoatchat/stoatchat/issues/670)) ([98c7b1b](https://github.com/stoatchat/stoatchat/commit/98c7b1b5a5b9fdac5c0ab83be10f0e23114dbfc9))
* load config from env vars ([#576](https://github.com/stoatchat/stoatchat/issues/576)) ([5191bd1](https://github.com/stoatchat/stoatchat/commit/5191bd16b2a905b8409838e34eb0baca96f08580))
* parse message push notification content and replace internal formatting ([#693](https://github.com/stoatchat/stoatchat/issues/693)) ([d1e72ce](https://github.com/stoatchat/stoatchat/commit/d1e72cee42c54e16f4e49af569897528b10a28ca))
* Transfer ownership ([#396](https://github.com/stoatchat/stoatchat/issues/396)) ([735d644](https://github.com/stoatchat/stoatchat/commit/735d644e043793cb86e74aab5b88bb4b8bc17ba2))
* update livekit ([#698](https://github.com/stoatchat/stoatchat/issues/698)) ([f181edc](https://github.com/stoatchat/stoatchat/commit/f181edc8f2ff3ce4b6d48938dfc73931ecfa2279))
### Bug Fixes
* add flag for disabling events instead of commenting them out ([#695](https://github.com/stoatchat/stoatchat/issues/695)) ([a5cd08a](https://github.com/stoatchat/stoatchat/commit/a5cd08a655dece4269f3ac84fa2387ae356709a5))
* add masquerade permission to default direct message settings ([#665](https://github.com/stoatchat/stoatchat/issues/665)) ([ab52569](https://github.com/stoatchat/stoatchat/commit/ab525699bd6663333f0e9fed6d2455e482e6a09f))
* Check for appropriate permission for removing other users avatar ([#657](https://github.com/stoatchat/stoatchat/issues/657)) ([d56135e](https://github.com/stoatchat/stoatchat/commit/d56135e0cbc713884c9378832952f7ad490fa315))
* default video resolution is a non-existent size ([#601](https://github.com/stoatchat/stoatchat/issues/601)) ([0698e11](https://github.com/stoatchat/stoatchat/commit/0698e115e8d003d615e468c4fb9654e6bbc9107f)), closes [#588](https://github.com/stoatchat/stoatchat/issues/588)
* **docs:** Update GitHub links ([#647](https://github.com/stoatchat/stoatchat/issues/647)) ([b830631](https://github.com/stoatchat/stoatchat/commit/b830631bd25a546844b7bdd30386084bb365e4de))
* don't use a bitop for OR ([#676](https://github.com/stoatchat/stoatchat/issues/676)) ([5701b5c](https://github.com/stoatchat/stoatchat/commit/5701b5c18c513f796af365169ceaea372a22638c))
* Fix typo for p256dh in vapid notification flow ([#622](https://github.com/stoatchat/stoatchat/issues/622)) ([a80ad1c](https://github.com/stoatchat/stoatchat/commit/a80ad1cbe58b8af5e45751e51d94d93c1cea1c9f))
* improve generated openapi.json ([#584](https://github.com/stoatchat/stoatchat/issues/584)) ([52ed510](https://github.com/stoatchat/stoatchat/commit/52ed5100c2446e0b261085639e123e7e124cab2c))
* no node state set on channel creation ([#653](https://github.com/stoatchat/stoatchat/issues/653)) ([24d0d2b](https://github.com/stoatchat/stoatchat/commit/24d0d2b7266f6f8a692d0a52704acfecf517674c))
* only show first line on commit messages ([#696](https://github.com/stoatchat/stoatchat/issues/696)) ([91783b9](https://github.com/stoatchat/stoatchat/commit/91783b906697fc85305dee683f7c15dda55f0c50))
* pass &str to Reference ([#697](https://github.com/stoatchat/stoatchat/issues/697)) ([ccda6f5](https://github.com/stoatchat/stoatchat/commit/ccda6f5c53ee043705f7ff6b5f6c393f020781de))
* redis_url vs redis_uri in config ([#666](https://github.com/stoatchat/stoatchat/issues/666)) ([b0b728f](https://github.com/stoatchat/stoatchat/commit/b0b728fb0dbc9ee28360301de1c3ea501bbbff1d))
* replace some links and Revolt mentions to current Stoat ([#515](https://github.com/stoatchat/stoatchat/issues/515)) ([d629e89](https://github.com/stoatchat/stoatchat/commit/d629e89304be2f0011e189293b278f07d346aa7d))
* send push notifications for DM and group messages ([#660](https://github.com/stoatchat/stoatchat/issues/660)) ([52c0d2f](https://github.com/stoatchat/stoatchat/commit/52c0d2f266b76d8975bba2d5e75c62bb30149c45))
* store server id in redis and in room metadata to be able to delete voice state in all scenarios ([#656](https://github.com/stoatchat/stoatchat/issues/656)) ([49c6289](https://github.com/stoatchat/stoatchat/commit/49c628958070e4f0a5edc764d3b48158589219d9))
* uname is missing from crond ([#675](https://github.com/stoatchat/stoatchat/issues/675)) ([dc4438b](https://github.com/stoatchat/stoatchat/commit/dc4438bc3c7b2cad8d442b3cd438afb9ed566a5e))
## [0.11.5](https://github.com/stoatchat/stoatchat/compare/v0.11.4...v0.11.5) (2026-02-17)

196
Cargo.lock generated
View File

@@ -4671,7 +4671,7 @@ dependencies = [
"log",
"parking_lot",
"pbjson-types",
"prost",
"prost 0.12.6",
"rand 0.9.2",
"reqwest 0.11.27",
"scopeguard",
@@ -4693,7 +4693,7 @@ dependencies = [
"parking_lot",
"pbjson",
"pbjson-types",
"prost",
"prost 0.12.6",
"prost-types",
"serde",
"thiserror 1.0.69",
@@ -5484,6 +5484,90 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "opentelemetry"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0"
dependencies = [
"futures-core",
"futures-sink",
"js-sys",
"pin-project-lite 0.2.16",
"thiserror 2.0.18",
"tracing",
]
[[package]]
name = "opentelemetry-appender-tracing"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef6a1ac5ca3accf562b8c306fa8483c85f4390f768185ab775f242f7fe8fdcc2"
dependencies = [
"opentelemetry",
"tracing",
"tracing-core",
"tracing-subscriber",
]
[[package]]
name = "opentelemetry-http"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d"
dependencies = [
"async-trait",
"bytes 1.10.1",
"http 1.3.1",
"opentelemetry",
"reqwest 0.12.23",
]
[[package]]
name = "opentelemetry-otlp"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f"
dependencies = [
"http 1.3.1",
"opentelemetry",
"opentelemetry-http",
"opentelemetry-proto",
"opentelemetry_sdk",
"prost 0.14.3",
"reqwest 0.12.23",
"thiserror 2.0.18",
"tracing",
]
[[package]]
name = "opentelemetry-proto"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f"
dependencies = [
"opentelemetry",
"opentelemetry_sdk",
"prost 0.14.3",
"tonic",
"tonic-prost",
]
[[package]]
name = "opentelemetry_sdk"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd"
dependencies = [
"futures-channel",
"futures-executor",
"futures-util",
"opentelemetry",
"percent-encoding",
"rand 0.9.2",
"thiserror 2.0.18",
]
[[package]]
name = "ordered-multimap"
version = "0.4.3"
@@ -5612,7 +5696,7 @@ checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735"
dependencies = [
"heck 0.4.1",
"itertools 0.11.0",
"prost",
"prost 0.12.6",
"prost-types",
]
@@ -5626,7 +5710,7 @@ dependencies = [
"chrono",
"pbjson",
"pbjson-build",
"prost",
"prost 0.12.6",
"prost-build",
"serde",
]
@@ -6209,7 +6293,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29"
dependencies = [
"bytes 1.10.1",
"prost-derive",
"prost-derive 0.12.6",
]
[[package]]
name = "prost"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568"
dependencies = [
"bytes 1.10.1",
"prost-derive 0.14.3",
]
[[package]]
@@ -6226,7 +6320,7 @@ dependencies = [
"once_cell",
"petgraph",
"prettyplease",
"prost",
"prost 0.12.6",
"prost-types",
"regex",
"syn 2.0.106",
@@ -6246,13 +6340,26 @@ dependencies = [
"syn 2.0.106",
]
[[package]]
name = "prost-derive"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b"
dependencies = [
"anyhow",
"itertools 0.14.0",
"proc-macro2",
"quote 1.0.40",
"syn 2.0.106",
]
[[package]]
name = "prost-types"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0"
dependencies = [
"prost",
"prost 0.12.6",
]
[[package]]
@@ -6742,7 +6849,9 @@ dependencies = [
"base64 0.22.1",
"bytes 1.10.1",
"encoding_rs",
"futures-channel",
"futures-core",
"futures-util",
"h2 0.4.12",
"http 1.3.1",
"http-body 1.0.1",
@@ -6798,7 +6907,7 @@ dependencies = [
[[package]]
name = "revolt-autumn"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"axum",
"axum-macros",
@@ -6812,6 +6921,10 @@ dependencies = [
"lazy_static",
"moka",
"nanoid",
"opentelemetry",
"opentelemetry-appender-tracing",
"opentelemetry-otlp",
"opentelemetry_sdk",
"revolt-config",
"revolt-database",
"revolt-files",
@@ -6837,7 +6950,7 @@ dependencies = [
[[package]]
name = "revolt-bonfire"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"async-channel 2.5.0",
"async-std",
@@ -6868,7 +6981,7 @@ dependencies = [
[[package]]
name = "revolt-coalesced"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"indexmap 2.13.0",
"lru 0.16.3",
@@ -6877,7 +6990,7 @@ dependencies = [
[[package]]
name = "revolt-config"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"async-std",
"cached",
@@ -6894,7 +7007,7 @@ dependencies = [
[[package]]
name = "revolt-crond"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"log",
"revolt-config",
@@ -6906,7 +7019,7 @@ dependencies = [
[[package]]
name = "revolt-database"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"amqprs",
"async-lock 2.8.0",
@@ -6956,7 +7069,7 @@ dependencies = [
[[package]]
name = "revolt-delta"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"amqprs",
"async-channel 1.9.0",
@@ -7006,7 +7119,7 @@ dependencies = [
[[package]]
name = "revolt-files"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"aes-gcm",
"anyhow",
@@ -7034,7 +7147,7 @@ dependencies = [
[[package]]
name = "revolt-gifbox"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"axum",
"axum-extra",
@@ -7057,7 +7170,7 @@ dependencies = [
[[package]]
name = "revolt-january"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"async-recursion",
"axum",
@@ -7085,7 +7198,7 @@ dependencies = [
[[package]]
name = "revolt-models"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"indexmap 1.9.3",
"iso8601-timestamp",
@@ -7104,14 +7217,14 @@ dependencies = [
[[package]]
name = "revolt-parser"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"logos",
]
[[package]]
name = "revolt-permissions"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"async-std",
"async-trait",
@@ -7126,7 +7239,7 @@ dependencies = [
[[package]]
name = "revolt-presence"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"async-std",
"log",
@@ -7138,7 +7251,7 @@ dependencies = [
[[package]]
name = "revolt-pushd"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"amqprs",
"anyhow",
@@ -7169,7 +7282,7 @@ dependencies = [
[[package]]
name = "revolt-ratelimits"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"async-trait",
"authifier",
@@ -7186,7 +7299,7 @@ dependencies = [
[[package]]
name = "revolt-result"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"axum",
"log",
@@ -7202,7 +7315,7 @@ dependencies = [
[[package]]
name = "revolt-voice-ingress"
version = "0.11.5"
version = "0.12.0"
dependencies = [
"amqprs",
"async-std",
@@ -9115,6 +9228,38 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "tonic"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec"
dependencies = [
"async-trait",
"base64 0.22.1",
"bytes 1.10.1",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
"percent-encoding",
"pin-project",
"sync_wrapper 1.0.2",
"tokio-stream",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tonic-prost"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309"
dependencies = [
"bytes 1.10.1",
"prost 0.14.3",
"tonic",
]
[[package]]
name = "totp-lite"
version = "2.0.1"
@@ -9157,6 +9302,7 @@ dependencies = [
"pin-project-lite 0.2.16",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]

View File

@@ -110,6 +110,18 @@ If you'd like to change anything, create a `Revolt.overrides.toml` file and spec
> ports: !override
> - "14072:5672"
> - "14672:15672"
>
> victoria-metrics:
> ports: !override
> - "18428:8428"
>
> victoria-logs:
> ports: !override
> - "19428:9428"
>
> victoria-traces:
> ports: !override
> - "14428:10428"
> ```
>
> And corresponding Revolt configuration:

View File

@@ -4,7 +4,7 @@
[database]
# MongoDB connection URL
# Defaults to the container name specified in self-hosted
mongodb = "mongodb://127.0.0.1:27017?directConnection=true&replicaSet=rs0"
mongodb = "mongodb://127.0.0.1:27017"
# Redis connection URL
# Defaults to the container name specified in self-hosted
redis = "redis://127.0.0.1:6379/"

View File

@@ -1,3 +1,5 @@
name: stoat-backend
services:
# Redis
redis:
@@ -8,29 +10,10 @@ services:
# MongoDB
database:
image: mongo
command: ["--replSet", "rs0", "--bind_ip_all"]
ports:
- "27017:27017"
volumes:
- ./.data/db:/data/db
- ./scripts/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
healthcheck:
test: >
mongosh --quiet --eval "
try {
const status = rs.status();
if (status.ok === 1 && status.members[0].stateStr === 'PRIMARY') {
quit(0);
} else {
quit(1);
}
} catch (e) {
quit(1);
}"
interval: 5s
timeout: 5s
retries: 10
start_period: 10s
ulimits:
nofile:
soft: 65536
@@ -88,9 +71,29 @@ services:
MAILDEV_INCOMING_USER: smtp
MAILDEV_INCOMING_PASS: smtp
# LiveKit
livekit:
image: ghcr.io/stoatchat/livekit-server:v1.9.13
command: --config /etc/livekit.yml
network_mode: "host"
volumes:
- ./livekit.yml:/etc/livekit.yml
# VM
victoria-metrics:
image: victoriametrics/victoria-metrics:latest
ports:
- 8428:8428
# VL
victoria-logs:
image: docker.io/victoriametrics/victoria-logs:v1.43.1
ports:
- 9428:9428
command: -storageDataPath=victoria-logs-data
# VT
victoria-traces:
image: docker.io/victoriametrics/victoria-traces:latest
ports:
- 10428:10428

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-bonfire"
version = "0.11.5"
version = "0.12.0"
license = "AGPL-3.0-or-later"
edition = "2021"
publish = false

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-coalesced"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>", "Zomatree <me@zomatree.live>"]

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-config"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -38,4 +38,4 @@ sentry = { version = "0.31.5", optional = true }
sentry-anyhow = { version = "0.38.1", optional = true }
# Core
revolt-result = { version = "0.11.5", path = "../result", optional = true }
revolt-result = { version = "0.12.0", path = "../result", optional = true }

View File

@@ -1,5 +1,5 @@
[database]
mongodb = "mongodb://localhost?directConnection=true&replicaSet=rs0"
mongodb = "mongodb://localhost"
redis = "redis://localhost/"
[rabbit]

View File

@@ -4,7 +4,7 @@ disable_events_dont_use = false
[database]
# MongoDB connection URL
# Defaults to the container name specified in self-hosted
mongodb = "mongodb://database?directConnection=true&replicaSet=rs0"
mongodb = "mongodb://database"
# Redis connection URL
# Defaults to the container name specified in self-hosted
redis = "redis://redis/"

View File

@@ -480,7 +480,6 @@ pub async fn setup_logging(release: &'static str, dsn: String) -> Option<sentry:
std::env::set_var("ROCKET_ADDRESS", "0.0.0.0");
}
pretty_env_logger::init();
log::info!("Starting {release}");
if dsn.is_empty() {

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-database"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -32,19 +32,19 @@ default = ["mongodb", "async-std-runtime", "tasks"]
[dependencies]
# Core
revolt-config = { version = "0.11.5", path = "../config", features = [
revolt-config = { version = "0.12.0", path = "../config", features = [
"report-macros",
] }
revolt-result = { version = "0.11.5", path = "../result" }
revolt-models = { version = "0.11.5", path = "../models", features = [
revolt-result = { version = "0.12.0", path = "../result" }
revolt-models = { version = "0.12.0", path = "../models", features = [
"validator",
] }
revolt-presence = { version = "0.11.5", path = "../presence" }
revolt-permissions = { version = "0.11.5", path = "../permissions", features = [
revolt-presence = { version = "0.12.0", path = "../presence" }
revolt-permissions = { version = "0.12.0", path = "../permissions", features = [
"serde",
"bson",
] }
revolt-parser = { version = "0.11.5", path = "../parser" }
revolt-parser = { version = "0.12.0", path = "../parser" }
# Utility
log = "0.4"

View File

@@ -705,7 +705,7 @@ impl Message {
Some(
PushNotification::from(
self.clone().into_model(user, member),
Some(author),
Some(author.clone()),
channel.to_owned().into(),
)
.await,
@@ -713,7 +713,11 @@ impl Message {
self.clone(),
match channel {
Channel::DirectMessage { recipients, .. }
| Channel::Group { recipients, .. } => recipients.clone(),
| Channel::Group { recipients, .. } => recipients
.iter()
.filter(|uid| *uid != author.id())
.cloned()
.collect(),
Channel::TextChannel { .. } => {
self.mentions.clone().unwrap_or_default()
}

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-files"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -27,10 +27,10 @@ typenum = "1.17.0"
aws-config = "1.5.5"
aws-sdk-s3 = { version = "1.46.0", features = ["behavior-version-latest"] }
revolt-config = { version = "0.11.5", path = "../config", features = [
revolt-config = { version = "0.12.0", path = "../config", features = [
"report-macros",
] }
revolt-result = { version = "0.11.5", path = "../result" }
revolt-result = { version = "0.12.0", path = "../result" }
# image processing
jxl-oxide = { workspace = true }

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-models"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -21,8 +21,8 @@ default = ["serde", "partials", "rocket"]
[dependencies]
# Core
revolt-config = { version = "0.11.5", path = "../config" }
revolt-permissions = { version = "0.11.5", path = "../permissions" }
revolt-config = { version = "0.12.0", path = "../config" }
revolt-permissions = { version = "0.12.0", path = "../permissions" }
# Utility
regex = "1.11"

View File

@@ -391,6 +391,7 @@ auto_derived!(
);
/// Message Author Abstraction
#[derive(Clone)]
pub enum MessageAuthor<'a> {
User(&'a User),
Webhook(&'a Webhook),

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-parser"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "MIT"
authors = ["Zomatree <me@zomatree.live>", "Paul Makles <me@insrt.uk>"]

View File

@@ -262,7 +262,7 @@ mod tests {
)
.collect::<Vec<_>>();
assert_eq!(output.len(), 6);
assert_eq!(output.len(), 7);
assert_eq!(output[0], MessageToken::CodeblockMarker(1));
assert_eq!(
output[1],

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-permissions"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -22,7 +22,7 @@ async-std = { version = "1.8.0", features = ["attributes"] }
[dependencies]
# Core
revolt-result = { version = "0.11.5", path = "../result" }
revolt-result = { version = "0.12.0", path = "../result" }
# Utility
auto_ops = "0.3.0"

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-presence"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -17,7 +17,7 @@ redis-is-patched = []
async-std = { version = "1.8.0", features = ["attributes"] }
# Config for loading Redis URI
revolt-config = { version = "0.11.5", path = "../config" }
revolt-config = { version = "0.12.0", path = "../config" }
[dependencies]
# Utility

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-ratelimits"
version = "0.11.5"
version = "0.12.0"
edition = "2024"
license = "MIT"
authors = ["Zomatree <me@zomatree.live>", "Paul Makles <me@insrt.uk>"]
@@ -18,9 +18,9 @@ axum = ["dep:axum", "revolt-database/axum-impl"]
default = ["rocket", "axum"]
[dependencies]
revolt-database = { version = "0.11.5", path = "../database" }
revolt-result = { version = "0.11.5", path = "../result" }
revolt-config = { version = "0.11.5", path = "../config" }
revolt-database = { version = "0.12.0", path = "../database" }
revolt-result = { version = "0.12.0", path = "../result" }
revolt-config = { version = "0.12.0", path = "../config" }
rocket = { version = "0.5.1", optional = true }
revolt_rocket_okapi = { version = "0.10.0", optional = true }

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-result"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-crond"
version = "0.11.5"
version = "0.12.0"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"]
edition = "2021"
@@ -17,7 +17,7 @@ log = "0.4"
tokio = { version = "1" }
# Core
revolt-database = { version = "0.11.5", path = "../../core/database" }
revolt-result = { version = "0.11.5", path = "../../core/result" }
revolt-config = { version = "0.11.5", path = "../../core/config" }
revolt-files = { version = "0.11.5", path = "../../core/files" }
revolt-database = { version = "0.12.0", path = "../../core/database" }
revolt-result = { version = "0.12.0", path = "../../core/result" }
revolt-config = { version = "0.12.0", path = "../../core/config" }
revolt-files = { version = "0.12.0", path = "../../core/files" }

View File

@@ -1,24 +1,24 @@
[package]
name = "revolt-pushd"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "AGPL-3.0-or-later"
publish = false
[dependencies]
revolt-result = { version = "0.11.5", path = "../../core/result" }
revolt-config = { version = "0.11.5", path = "../../core/config", features = [
revolt-result = { version = "0.12.0", path = "../../core/result" }
revolt-config = { version = "0.12.0", path = "../../core/config", features = [
"report-macros",
"anyhow",
] }
revolt-database = { version = "0.11.5", path = "../../core/database" }
revolt-models = { version = "0.11.5", path = "../../core/models", features = [
revolt-database = { version = "0.12.0", path = "../../core/database" }
revolt-models = { version = "0.12.0", path = "../../core/models", features = [
"validator",
] }
revolt-presence = { version = "0.11.5", path = "../../core/presence", features = [
revolt-presence = { version = "0.12.0", path = "../../core/presence", features = [
"redis-is-patched",
] }
revolt-parser = { version = "0.11.5", path = "../../core/parser" }
revolt-parser = { version = "0.12.0", path = "../../core/parser" }
anyhow = { version = "1.0.98" }

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-voice-ingress"
version = "0.11.5"
version = "0.12.0"
license = "AGPL-3.0-or-later"
edition = "2021"
publish = false

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-delta"
version = "0.11.5"
version = "0.12.0"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <paulmakles@gmail.com>"]
edition = "2018"

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-autumn"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "AGPL-3.0-or-later"
publish = false
@@ -40,29 +40,33 @@ serde = { version = "1.0", features = ["derive"] }
# Async runtime
tokio = { version = "1.0", features = ["full"] }
# Logging
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Core crates
revolt-files = { version = "0.11.5", path = "../../core/files" }
revolt-config = { version = "0.11.5", path = "../../core/config" }
revolt-database = { version = "0.11.5", path = "../../core/database", features = [
revolt-files = { version = "0.12.0", path = "../../core/files" }
revolt-config = { version = "0.12.0", path = "../../core/config" }
revolt-database = { version = "0.12.0", path = "../../core/database", features = [
"axum-impl",
] }
revolt-result = { version = "0.11.5", path = "../../core/result", features = [
revolt-result = { version = "0.12.0", path = "../../core/result", features = [
"utoipa",
"axum",
] }
revolt-ratelimits = { version = "0.11.5", path = "../../core/ratelimits", features = ["axum"] }
revolt-ratelimits = { version = "0.12.0", path = "../../core/ratelimits", features = ["axum"] }
# Axum / web server
tempfile = "3.12.0"
axum-macros = "0.4.1"
axum_typed_multipart = "0.12.1"
axum = { version = "0.7.5", features = ["multipart"] }
tower-http = { version = "0.5.2", features = ["cors"] }
axum-macros = { workspace = true }
axum_typed_multipart = { workspace = true }
axum = { workspace = true }
tower-http = { workspace = true }
# OpenAPI & documentation generation
utoipa-scalar = { version = "0.1.0", features = ["axum"] }
utoipa = { version = "4.2.3", features = ["axum_extras", "ulid"] }
# OpenTelemetry
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
opentelemetry = { workspace = true }
opentelemetry_sdk = { workspace = true }
opentelemetry-otlp = { workspace = true }
opentelemetry-appender-tracing = { workspace = true }

View File

@@ -27,7 +27,6 @@ use utoipa::ToSchema;
use crate::{exif::strip_metadata, metadata::generate_metadata, mime_type::determine_mime_type, AppState};
/// Build the API router
pub async fn router() -> Router<AppState> {
let config = config().await;
@@ -58,8 +57,6 @@ pub async fn router() -> Router<AppState> {
}
lazy_static! {
/// Short-lived file cache to allow us to populate different CDN regions without increasing bandwidth to S3 provider
/// Uploads will also be stored here to prevent immediately queued downloads from doing the entire round-trip
static ref S3_CACHE: moka::future::Cache<String, Result<Vec<u8>>> = moka::future::Cache::builder()
.weigher(|_key, value: &Result<Vec<u8>>| -> u32 {
std::mem::size_of::<Result<Vec<u8>>>() as u32 + if let Ok(vec) = value {
@@ -76,7 +73,6 @@ lazy_static! {
.build();
}
/// Retrieve hash information and file data by given hash
async fn retrieve_file_by_hash(hash: &FileHash) -> Result<Vec<u8>> {
if let Some(data) = S3_CACHE.get(&hash.id).await {
data
@@ -87,17 +83,15 @@ async fn retrieve_file_by_hash(hash: &FileHash) -> Result<Vec<u8>> {
}
}
/// Successful root response
#[derive(Serialize, Debug, ToSchema)]
pub struct RootResponse {
autumn: &'static str,
version: &'static str,
}
/// Capture crate version from Cargo
static CRATE_VERSION: &str = env!("CARGO_PKG_VERSION");
/// Root response from service
/// Get information about the service
#[utoipa::path(
get,
path = "/",
@@ -112,7 +106,6 @@ async fn root() -> Json<RootResponse> {
})
}
/// Empty handler for OPTIONS routes
async fn options() {}
/// Available tags to upload to
@@ -176,28 +169,19 @@ async fn upload_file(
Path(tag): Path<Tag>,
TypedMultipart(UploadPayload { mut file }): TypedMultipart<UploadPayload>,
) -> Result<Json<UploadResponse>> {
// Fetch configuration
let now = Instant::now();
let config = config().await;
// Keep track of processing time
let now = Instant::now();
// Extract the filename, or give it a generic name
let filename = file.metadata.file_name.unwrap_or("unnamed-file".to_owned());
// Load file to memory
let mut buf = Vec::<u8>::new();
report_internal_error!(file.contents.read_to_end(&mut buf))?;
// Take note of original file size
let original_file_size = buf.len();
// Ensure the file is not empty
if original_file_size < config.files.limit.min_file_size {
return Err(create_error!(FileTooSmall));
}
// Get user's file upload limits
let limits = user.limits().await;
let size_limit = *limits
.file_upload_size_limit
@@ -208,24 +192,19 @@ async fn upload_file(
return Err(create_error!(FileTooLarge { max: size_limit }));
}
// Generate sha256 hash
let original_hash = {
let mut hasher = sha2::Sha256::new();
hasher.update(&buf);
hasher.finalize()
};
// Generate an ID for this file
let id = if matches!(tag, Tag::emojis) {
ulid::Ulid::new().to_string()
} else {
nanoid::nanoid!(42)
};
// Determine the mime type for the file
let mime_type = determine_mime_type(&mut file.contents, &buf, &filename);
// Check blocklist for mime type
if config
.files
.blocked_mime_types
@@ -235,15 +214,11 @@ async fn upload_file(
return Err(create_error!(FileTypeNotAllowed));
}
// Determine metadata for the file
let metadata = generate_metadata(&file.contents, mime_type);
// Block non-images for non-attachment uploads
if !matches!(tag, Tag::attachments) && !matches!(metadata, Metadata::Image { .. }) {
return Err(create_error!(FileTypeNotAllowed));
}
// Find an existing hash and use that if possible
let file_hash_exists = if let Ok(file_hash) = db
.fetch_attachment_hash(&format!("{original_hash:02x}"))
.await
@@ -266,10 +241,8 @@ async fn upload_file(
false
};
// Strip metadata
let (buf, metadata) = strip_metadata(file.contents, buf, metadata, mime_type).await?;
// Virus scan files if ClamAV is configured
if matches!(metadata, Metadata::File)
&& (config.files.scan_mime_types.is_empty()
|| config.files.scan_mime_types.iter().any(|v| v == mime_type))
@@ -278,7 +251,6 @@ async fn upload_file(
return Err(create_error!(InternalError));
}
// Print file information for debug purposes
let new_file_size = buf.len() + AUTHENTICATION_TAG_SIZE_BYTES;
let processed_hash = {
let mut hasher = sha2::Sha256::new();
@@ -290,7 +262,6 @@ async fn upload_file(
tracing::info!("Received file {filename}\nOriginal hash: {original_hash:02x}\nOriginal size: {original_file_size} bytes\nMime type: {mime_type}\nMetadata: {metadata:?}\nProcessed file size: {new_file_size} bytes ({:.2}%).\nProcessed hash: {processed_hash:02x}\nProcessing took {time_to_process:?}", process_ratio * 100.0);
// Create hash entry in database
let file_hash = FileHash {
id: format!("{original_hash:02x}"),
processed_hash: format!("{processed_hash:02x}"),
@@ -306,21 +277,17 @@ async fn upload_file(
size: new_file_size as isize,
};
// Add attachment hash if it doesn't exist
if !file_hash_exists {
db.insert_attachment_hash(&file_hash).await?;
}
// Upload the file to S3 and commit nonce to database
let upload_start = Instant::now();
let nonce = upload_to_s3(&file_hash.bucket_id, &file_hash.id, &buf).await?;
db.set_attachment_hash_nonce(&file_hash.id, &nonce).await?;
// Debug information
let time_to_upload = Instant::now() - upload_start;
tracing::info!("Took {time_to_upload:?} to upload {new_file_size} bytes to S3.");
// Finally, create the file and return its ID
let tag: &'static str = tag.into();
db.insert_attachment(&file_hash.into_file(id.clone(), tag.to_owned(), filename, user.id))
.await?;
@@ -368,12 +335,10 @@ async fn fetch_preview(
let tag_str: &'static str = tag.clone().into();
let file = db.fetch_attachment(tag_str, &file_id).await?;
// Ignore deleted files
if file.deleted.is_some_and(|v| v) {
return Err(create_error!(NotFound));
}
// Ignore files that haven't been attached
if file.used_for.is_none() {
return Err(create_error!(NotFound));
}
@@ -382,7 +347,7 @@ async fn fetch_preview(
let is_animated = matches!(hash.metadata, Metadata::Image { animated: true, .. });
// Only process image files and don't process GIFs if not avatar or icon
// Process GIFs if avatar or icon
if !matches!(hash.metadata, Metadata::Image { .. })
|| (is_animated && !matches!(tag, Tag::avatars | Tag::icons))
{
@@ -391,10 +356,8 @@ async fn fetch_preview(
);
}
// Original image data
let data = retrieve_file_by_hash(&hash).await?;
// Read image and create thumbnail
let data = create_thumbnail(
decode_image(&mut Cursor::new(data), &file.content_type)?,
tag_str,
@@ -436,17 +399,14 @@ async fn fetch_file(
let tag: &'static str = tag.clone().into();
let file = db.fetch_attachment(tag, &file_id).await?;
// Ignore deleted files
if file.deleted.is_some_and(|v| v) {
return Err(create_error!(NotFound));
}
// Ignore files that haven't been attached
if file.used_for.is_none() {
return Err(create_error!(NotFound));
}
// Ensure filename is correct
if file_name != file.filename {
if file_name == "original" {
return Ok(

View File

@@ -3,7 +3,6 @@ use std::time::Duration;
use revolt_config::{config, report_internal_error};
use revolt_result::Result;
/// Initialise ClamAV
pub async fn init() {
let config = config().await;
@@ -32,7 +31,6 @@ pub async fn init() {
}
}
/// Scan for malware
pub async fn is_malware(buf: &[u8]) -> Result<bool> {
let config = config().await;
if config.files.clamd_host.is_empty() {

View File

@@ -8,7 +8,6 @@ use revolt_result::{create_error, Result};
use tempfile::NamedTempFile;
use tokio::process::Command;
/// Strip EXIF data from given file and produce new file and metadata
pub async fn strip_metadata(
file: NamedTempFile,
buf: Vec<u8>,
@@ -42,20 +41,16 @@ pub async fn strip_metadata(
// }
// Apply orientation manually & strip all other EXIF data
"image/jpeg" | "image/png" | "image/avif" | "image/tiff" => {
// Create a reader
let mut cursor = Cursor::new(buf);
// Decode the image
let image = report_internal_error!(report_internal_error!(ImageReader::new(
&mut cursor
)
.with_guessed_format())?
.decode());
// Reset read position
cursor.set_position(0);
// Extract orientation data
let exif_reader = Reader::new();
let rotation = match exif_reader.read_from_container(&mut cursor) {
Ok(exif) => match exif.get_field(exif::Tag::Orientation, exif::In::PRIMARY) {
@@ -65,12 +60,10 @@ pub async fn strip_metadata(
_ => 0,
};
// Create a buffer to write to
let mut bytes: Vec<u8> = Vec::new();
let mut writer = Cursor::new(&mut bytes);
// Apply the EXIF rotation
// See https://jdhao.github.io/2019/07/31/image_rotation_exif_info/
// https://jdhao.github.io/2019/07/31/image_rotation_exif_info/
report_internal_error!(match &rotation {
2 => image?.fliph(),
3 => image?.rotate180(),
@@ -92,7 +85,6 @@ pub async fn strip_metadata(
},
))?;
// Calculate dimensions after rotation.
let (width, height) = match &rotation {
2 | 4 | 5 | 7 => (*height, *width),
_ => (*width, *height),
@@ -108,16 +100,13 @@ pub async fn strip_metadata(
},
))
}
// JXLs store EXIF data but we don't have the ability to write them
// TODO: JXLs store EXIF data but we don't have the ability to write them
"image/jxl" => Ok((buf, metadata)),
// All other images that cannot store EXIF data
// assume all other images that cannot store EXIF data
_ => Ok((buf, metadata)),
},
// Use ffmpeg to copy video stream and probe new metadata
Metadata::Video { .. } => match mime {
// Strip EXIF data by copying video stream
"video/mp4" | "video/webm" | "video/quicktime" => {
// Pick the correct file format for ffmpeg
let ext = match mime {
"video/mp4" => "mp4",
"video/webm" => "webm",
@@ -125,30 +114,22 @@ pub async fn strip_metadata(
_ => unreachable!(),
};
// Temporary output file
let mut out_file = report_internal_error!(NamedTempFile::new())?;
// Process the file with ffmpeg
report_internal_error!(
Command::new("ffmpeg")
.args([
// Overwrite the temporary file
"-y",
// Read original uploaded file
"-i",
file.path().to_str().ok_or(create_error!(InternalError))?,
// Strip any metadata
"-map_metadata",
"-map_metadata", // strip metadata
"-1",
// Copy video / audio data to new file
"-c:v",
"-c:v", // just copy the streams
"copy",
"-c:a",
"copy",
// Select correct file format
"-f",
ext,
// Save to new temporary file
out_file
.path()
.to_str()
@@ -158,19 +139,17 @@ pub async fn strip_metadata(
.await
)?;
// Probe the file again
let metadata = crate::metadata::generate_metadata(&out_file, mime);
// Read the file from disk
let mut buf = Vec::<u8>::new();
report_internal_error!(out_file.read_to_end(&mut buf))?;
Ok((buf, metadata))
}
// Assume all other video formats cannot store EXIF data
// assume all other video formats cannot store EXIF data
_ => Ok((buf, metadata)),
},
// all other file types don't store EXIF data
// assume all other file types don't store EXIF data
_ => Ok((buf, metadata)),
}
}

View File

@@ -6,6 +6,7 @@ use axum_macros::FromRef;
use revolt_database::{Database, DatabaseInfo};
use revolt_ratelimits::axum as ratelimiter;
use tokio::net::TcpListener;
use tower_http::trace::TraceLayer;
use utoipa::{
openapi::security::{ApiKey, ApiKeyValue, SecurityScheme},
Modify, OpenApi,
@@ -27,13 +28,32 @@ struct AppState {
#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
// Configure logging and environment
revolt_config::configure!(files);
let logger_provider = init_logs();
// Wait for ClamAV
let otel_layer = OpenTelemetryTracingBridge::new(&logger_provider);
let filter_otel = EnvFilter::new("info")
.add_directive("hyper=off".parse().unwrap())
.add_directive("tonic=off".parse().unwrap())
.add_directive("h2=off".parse().unwrap())
.add_directive("reqwest=off".parse().unwrap());
let otel_layer = otel_layer.with_filter(filter_otel);
let filter_fmt = EnvFilter::new("info");
let fmt_layer = tracing_subscriber::fmt::layer()
.with_thread_names(true)
.with_filter(filter_fmt);
tracing_subscriber::registry()
.with(otel_layer)
.with(fmt_layer)
.init();
revolt_config::configure!(files);
clamav::init().await;
// Configure API schema
#[derive(OpenApi)]
#[openapi(
modifiers(&SecurityAddon),
@@ -76,7 +96,6 @@ async fn main() -> Result<(), std::io::Error> {
}
}
// Connect to the database
let db = DatabaseInfo::Auto.connect().await.unwrap();
let ratelimits = ratelimiter::RatelimitStorage::new(ratelimits::AutumnRatelimits);
@@ -85,7 +104,6 @@ async fn main() -> Result<(), std::io::Error> {
ratelimit_storage: ratelimits,
};
// Configure Axum and router
let app = Router::new()
.merge(Scalar::with_url("/scalar", ApiDoc::openapi()))
.nest("/", api::router().await)
@@ -94,10 +112,56 @@ async fn main() -> Result<(), std::io::Error> {
state.clone(),
ratelimiter::ratelimit_middleware,
))
.layer(TraceLayer::new_for_http())
.with_state(state);
// Configure TCP listener and bind
let address = SocketAddr::from((Ipv4Addr::UNSPECIFIED, 14704));
let listener = TcpListener::bind(&address).await?;
axum::serve(listener, app.into_make_service()).await
axum::serve(listener, app.into_make_service()).await?;
if let Err(e) = logger_provider.shutdown() {
panic!("logger provider failed to shut down");
}
Ok(())
}
use opentelemetry::trace::{TraceContextExt, Tracer};
use opentelemetry::KeyValue;
use opentelemetry::{global, InstrumentationScope};
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_otlp::{LogExporter, MetricExporter, Protocol, SpanExporter, WithExportConfig};
use opentelemetry_sdk::logs::SdkLoggerProvider;
use opentelemetry_sdk::metrics::SdkMeterProvider;
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::Resource;
use std::error::Error;
use std::sync::OnceLock;
use tracing::info;
use tracing_subscriber::prelude::*;
use tracing_subscriber::EnvFilter;
fn get_resource() -> Resource {
static RESOURCE: OnceLock<Resource> = OnceLock::new();
RESOURCE
.get_or_init(|| {
Resource::builder()
.with_service_name("basic-otlp-example-grpc")
.build()
})
.clone()
}
fn init_logs() -> SdkLoggerProvider {
let exporter = LogExporter::builder()
.with_http()
.with_endpoint("http://localhost:19428/insert/opentelemetry/v1/logs")
.with_protocol(Protocol::HttpBinary)
.build()
.expect("Failed to create log exporter");
SdkLoggerProvider::builder()
.with_resource(get_resource())
.with_batch_exporter(exporter)
.build()
}

View File

@@ -20,7 +20,6 @@ static SUPPORTED_IMAGE_MIME: [&str; 9] = [
"image/webp",
];
/// Generate metadata from file, using mime type as a hint
pub fn generate_metadata(f: &NamedTempFile, mime_type: &str) -> Metadata {
if SUPPORTED_IMAGE_MIME.contains(&mime_type) {
image_size(f)
@@ -56,7 +55,6 @@ pub fn generate_metadata(f: &NamedTempFile, mime_type: &str) -> Metadata {
}
}
/// Subroutine to ensure data isn't corrupted
pub fn validate_from_metadata(
reader: Cursor<Vec<u8>>,
metadata: Metadata,

View File

@@ -1,15 +1,12 @@
use tempfile::NamedTempFile;
/// Determine the mime type of the given temporary file and filename
pub fn determine_mime_type(f: &mut NamedTempFile, buf: &[u8], file_name: &str) -> &'static str {
// Force certain extensions into particular mime types
if file_name.to_lowercase().ends_with(".apk") {
return "application/vnd.android.package-archive";
} else if file_name.to_lowercase().ends_with(".exe") {
return "application/vnd.microsoft.portable-executable";
}
// Use magic signatures to determine mime type
let kind = infer::get_from_path(f.path()).expect("file read successfully");
let mime_type = if let Some(kind) = kind {
kind.mime_type()
@@ -17,7 +14,6 @@ pub fn determine_mime_type(f: &mut NamedTempFile, buf: &[u8], file_name: &str) -
"application/octet-stream"
};
// See if the file is actually just plain Unicode/ASCII text
if mime_type == "application/octet-stream" && simdutf8::basic::from_utf8(buf).is_ok() {
if file_name.to_lowercase().ends_with(".svg") {
return "image/svg+xml";

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-gifbox"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "AGPL-3.0-or-later"
publish = false
@@ -17,19 +17,19 @@ tokio = { version = "1.0", features = ["full"] }
reqwest = { version = "0.12", features = ["json"] }
# Core crates
revolt-config = { version = "0.11.5", path = "../../core/config" }
revolt-models = { version = "0.11.5", path = "../../core/models" }
revolt-result = { version = "0.11.5", path = "../../core/result", features = [
revolt-config = { version = "0.12.0", path = "../../core/config" }
revolt-models = { version = "0.12.0", path = "../../core/models" }
revolt-result = { version = "0.12.0", path = "../../core/result", features = [
"utoipa",
"axum",
] }
revolt-coalesced = { version = "0.11.5", path = "../../core/coalesced", features = [
revolt-coalesced = { version = "0.12.0", path = "../../core/coalesced", features = [
"queue",
] }
revolt-database = { version = "0.11.5", path = "../../core/database", features = [
revolt-database = { version = "0.12.0", path = "../../core/database", features = [
"axum-impl",
] }
revolt-ratelimits = { version = "0.11.5", path = "../../core/ratelimits", features = [
revolt-ratelimits = { version = "0.12.0", path = "../../core/ratelimits", features = [
"axum",
] }

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-january"
version = "0.11.5"
version = "0.12.0"
edition = "2021"
license = "AGPL-3.0-or-later"
publish = false
@@ -33,13 +33,13 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Core crates
revolt-config = { version = "0.11.5", path = "../../core/config" }
revolt-models = { version = "0.11.5", path = "../../core/models" }
revolt-result = { version = "0.11.5", path = "../../core/result", features = [
revolt-config = { version = "0.12.0", path = "../../core/config" }
revolt-models = { version = "0.12.0", path = "../../core/models" }
revolt-result = { version = "0.12.0", path = "../../core/result", features = [
"utoipa",
"axum",
] }
revolt-files = { version = "0.11.5", path = "../../core/files" }
revolt-files = { version = "0.12.0", path = "../../core/files" }
# Axum / web server
axum = { version = "0.7.5" }

View File

@@ -205,6 +205,11 @@
"path": "crates/daemons/pushd/Cargo.toml",
"jsonpath": "$.dependencies['revolt-presence'].version"
},
{
"type": "toml",
"path": "crates/daemons/pushd/Cargo.toml",
"jsonpath": "$.dependencies['revolt-parser'].version"
},
{
"type": "toml",
"path": "crates/daemons/voice-ingress/Cargo.toml",

View File

@@ -1,5 +0,0 @@
try {
rs.status();
} catch (e) {
rs.initiate({ _id: "rs0", members: [{ _id: 0, host: "localhost:27017" }] });
}

View File

@@ -1 +1 @@
0.11.5
0.12.0