forked from didirus/AstralRinth
Update Rust & Java dependencies (#4540)
* Update Java dependencies * Baselint lint fixes * Update Rust version * Update actix-files 0.6.6 -> 0.6.8 * Update actix-http 3.11.0 -> 3.11.2 * Update actix-rt 2.10.0 -> 2.11.0 * Update async_zip 0.0.17 -> 0.0.18 * Update async-compression 0.4.27 -> 0.4.32 * Update async-trait 0.1.88 -> 0.1.89 * Update async-tungstenite 0.30.0 -> 0.31.0 * Update const_format 0.2.34 -> 0.2.35 * Update bitflags 2.9.1 -> 2.9.4 * Update bytemuck 1.23.1 -> 1.24.0 * Update typed-path 0.11.0 -> 0.12.0 * Update chrono 0.4.41 -> 0.4.42 * Update cidre 0.11.2 -> 0.11.3 * Update clap 4.5.43 -> 4.5.48 * Update data-url 0.3.1 -> 0.3.2 * Update discord-rich-presence 0.2.5 -> 1.0.0 * Update enumset 1.1.7 -> 1.1.10 * Update flate2 1.1.2 -> 1.1.4 * Update hyper 1.6.0 -> 1.7.0 * Update hyper-util 0.1.16 -> 0.1.17 * Update iana-time-zone 0.1.63 -> 0.1.64 * Update image 0.25.6 -> 0.25.8 * Update indexmap 2.10.0 -> 2.11.4 * Update json-patch 4.0.0 -> 4.1.0 * Update meilisearch-sdk 0.29.1 -> 0.30.0 * Update clickhouse 0.13.3 -> 0.14.0 * Fix some prettier things * Update lettre 0.11.18 -> 0.11.19 * Update phf 0.12.1 -> 0.13.1 * Update png 0.17.16 -> 0.18.0 * Update quick-xml 0.38.1 -> 0.38.3 * Update redis 0.32.4 -> 0.32.7 * Update regex 1.11.1 -> 1.11.3 * Update reqwest 0.12.22 -> 0.12.23 * Update rust_decimal 1.37.2 -> 1.38.0 * Update rust-s3 0.35.1 -> 0.37.0 * Update serde 1.0.219 -> 1.0.228 * Update serde_bytes 0.11.17 -> 0.11.19 * Update serde_json 1.0.142 -> 1.0.145 * Update serde_with 3.14.0 -> 3.15.0 * Update sentry 0.42.0 -> 0.45.0 and sentry-actix 0.42.0 -> 0.45.0 * Update spdx 0.10.9 -> 0.12.0 * Update sysinfo 0.36.1 -> 0.37.2 * Update tauri 2.7.0 -> 2.8.5 * Update tauri-build 2.3.1 -> 2.4.1 * Update tauri-plugin-deep-link 2.4.1 -> 2.4.3 * Update tauri-plugin-dialog 2.3.2 -> 2.4.0 * Update tauri-plugin-http 2.5.1 -> 2.5.2 * Update tauri-plugin-opener 2.4.0 -> 2.5.0 * Update tauri-plugin-os 2.3.0 -> 2.3.1 * Update tauri-plugin-single-instance 2.3.2 -> 2.3.4 * Update tempfile 3.20.0 -> 3.23.0 * Update thiserror 2.0.12 -> 2.0.17 * Update tracing-subscriber 0.3.19 -> 0.3.20 * Update url 2.5.4 -> 2.5.7 * Update uuid 1.17.0 -> 1.18.1 * Update webp 0.3.0 -> 0.3.1 * Update whoami 1.6.0 -> 1.6.1 * Note that windows and windows-core can't be updated yet * Update zbus 5.9.0 -> 5.11.0 * Update zip 4.3.0 -> 6.0.0 * Fix build * Enforce rustls crypto provider * Refresh Cargo.lock * Update transitive dependencies * Bump Gradle usage to Java 17 * Use ubuntu-latest consistently across workflows * Fix lint * Fix lint in Rust * Update native-dialog 0.9.0 -> 0.9.2 * Update regex 1.11.3 -> 1.12.2 * Update reqwest 0.12.23 -> 0.12.24 * Update rust_decimal 1.38.0 -> 1.39.0 * Remaining lock-only updates * chore: move TLS impl of some other dependencies to aws-lc-rs The AWS bloatware "virus" expands by sheer force of widespread adoption by the ecosystem... 🫣 * chore(fmt): run Tombi --------- Co-authored-by: Alejandro González <me@alegon.dev>
This commit is contained in:
2
.github/workflows/i18n-pull.yml
vendored
2
.github/workflows/i18n-pull.yml
vendored
@@ -11,7 +11,7 @@ concurrency:
|
||||
jobs:
|
||||
pull_translations:
|
||||
name: 'Pull translations from Crowdin'
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main'
|
||||
concurrency:
|
||||
group: i18n-pull:${{ github.ref }}
|
||||
|
||||
2
.github/workflows/i18n-push.yml
vendored
2
.github/workflows/i18n-push.yml
vendored
@@ -18,7 +18,7 @@ concurrency:
|
||||
jobs:
|
||||
push_translations:
|
||||
name: Push sources to Crowdin
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main'
|
||||
concurrency:
|
||||
group: i18n-push:${{ github.ref }}
|
||||
|
||||
6
.github/workflows/theseus-build.yml
vendored
6
.github/workflows/theseus-build.yml
vendored
@@ -28,13 +28,13 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [macos-latest, windows-latest, ubuntu-22.04]
|
||||
platform: [macos-latest, windows-latest, ubuntu-latest]
|
||||
include:
|
||||
- platform: macos-latest
|
||||
artifact-target-name: universal-apple-darwin
|
||||
- platform: windows-latest
|
||||
artifact-target-name: x86_64-pc-windows-msvc
|
||||
- platform: ubuntu-22.04
|
||||
- platform: ubuntu-latest
|
||||
artifact-target-name: x86_64-unknown-linux-gnu
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
@@ -127,7 +127,7 @@ jobs:
|
||||
run: |
|
||||
[System.Convert]::FromBase64String("$env:DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_BASE64") | Set-Content -Path signer-client-cert.p12 -AsByteStream
|
||||
$env:DIGICERT_ONE_SIGNER_CREDENTIALS = "$env:DIGICERT_ONE_SIGNER_API_KEY|$PWD\signer-client-cert.p12|$env:DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_PASSWORD"
|
||||
$env:JAVA_HOME = "$env:JAVA_HOME_11_X64"
|
||||
$env:JAVA_HOME = "$env:JAVA_HOME_17_X64"
|
||||
pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json --verbose --bundles 'nsis,updater'
|
||||
Remove-Item -Path signer-client-cert.p12 -ErrorAction SilentlyContinue
|
||||
if: startsWith(matrix.platform, 'windows')
|
||||
|
||||
2
.github/workflows/turbo-ci.yml
vendored
2
.github/workflows/turbo-ci.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Lint and Test
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
# Ensure pnpm output is colored in GitHub Actions logs
|
||||
|
||||
691
Cargo.lock
generated
691
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
132
Cargo.toml
132
Cargo.toml
@@ -16,84 +16,84 @@ edition = "2024"
|
||||
|
||||
[workspace.dependencies]
|
||||
actix-cors = "0.7.1"
|
||||
actix-files = "0.6.6"
|
||||
actix-http = "3.11.0"
|
||||
actix-files = "0.6.8"
|
||||
actix-http = "3.11.2"
|
||||
actix-multipart = "0.7.2"
|
||||
actix-rt = "2.10.0"
|
||||
actix-rt = "2.11.0"
|
||||
actix-web = "4.11.0"
|
||||
actix-web-prom = "0.10.0"
|
||||
actix-ws = "0.3.0"
|
||||
argon2 = { version = "0.5.3", features = ["std"] }
|
||||
ariadne = { path = "packages/ariadne" }
|
||||
async-compression = { version = "0.4.27", default-features = false }
|
||||
async-compression = { version = "0.4.32", default-features = false }
|
||||
async-recursion = "1.1.1"
|
||||
async-stripe = { version = "0.41.0", default-features = false, features = [
|
||||
"runtime-tokio-hyper-rustls",
|
||||
] }
|
||||
async-trait = "0.1.88"
|
||||
async-tungstenite = { version = "0.30.0", default-features = false, features = [
|
||||
async-trait = "0.1.89"
|
||||
async-tungstenite = { version = "0.31.0", default-features = false, features = [
|
||||
"futures-03-sink",
|
||||
] }
|
||||
async-walkdir = "2.1.0"
|
||||
async_zip = "0.0.17"
|
||||
async_zip = "0.0.18"
|
||||
base64 = "0.22.1"
|
||||
bitflags = "2.9.1"
|
||||
bytemuck = "1.23.1"
|
||||
bitflags = "2.9.4"
|
||||
bytemuck = "1.24.0"
|
||||
bytes = "1.10.1"
|
||||
censor = "0.3.0"
|
||||
chardetng = "0.1.17"
|
||||
chrono = "0.4.41"
|
||||
cidre = { version = "0.11.2", default-features = false, features = [
|
||||
chrono = "0.4.42"
|
||||
cidre = { version = "0.11.3", default-features = false, features = [
|
||||
"macos_15_0",
|
||||
] }
|
||||
clap = "4.5.43"
|
||||
clickhouse = "0.13.3"
|
||||
clap = "4.5.48"
|
||||
clickhouse = "0.14.0"
|
||||
color-eyre = "0.6.5"
|
||||
color-thief = "0.2.2"
|
||||
console-subscriber = "0.4.1"
|
||||
const_format = "0.2.34"
|
||||
daedalus = { path = "packages/daedalus" }
|
||||
dashmap = "6.1.0"
|
||||
data-url = "0.3.1"
|
||||
data-url = "0.3.2"
|
||||
deadpool-redis = "0.22.0"
|
||||
derive_more = "2.0.1"
|
||||
dirs = "6.0.0"
|
||||
discord-rich-presence = "0.2.5"
|
||||
discord-rich-presence = "1.0.0"
|
||||
dotenv-build = "0.1.1"
|
||||
dotenvy = "0.15.7"
|
||||
dunce = "1.0.5"
|
||||
either = "1.15.0"
|
||||
encoding_rs = "0.8.35"
|
||||
enumset = "1.1.7"
|
||||
enumset = "1.1.10"
|
||||
eyre = "0.6.12"
|
||||
flate2 = "1.1.2"
|
||||
flate2 = "1.1.4"
|
||||
fs4 = { version = "0.13.1", default-features = false }
|
||||
futures = { version = "0.3.31", default-features = false }
|
||||
futures = "0.3.31"
|
||||
futures-util = "0.3.31"
|
||||
heck = "0.5.0"
|
||||
hex = "0.4.3"
|
||||
hickory-resolver = "0.25.2"
|
||||
hmac = "0.12.1"
|
||||
hyper = "1.6.0"
|
||||
hyper = "1.7.0"
|
||||
hyper-rustls = { version = "0.27.7", default-features = false, features = [
|
||||
"aws-lc-rs",
|
||||
"http1",
|
||||
"native-tokio",
|
||||
"ring",
|
||||
"tls12",
|
||||
] }
|
||||
hyper-util = "0.1.16"
|
||||
iana-time-zone = "0.1.63"
|
||||
image = { version = "0.25.6", default-features = false, features = ["rayon"] }
|
||||
indexmap = "2.10.0"
|
||||
hyper-util = "0.1.17"
|
||||
iana-time-zone = "0.1.64"
|
||||
image = { version = "0.25.8", default-features = false, features = ["rayon"] }
|
||||
indexmap = "2.11.4"
|
||||
indicatif = "0.18.0"
|
||||
itertools = "0.14.0"
|
||||
jemalloc_pprof = "0.8.1"
|
||||
json-patch = { version = "4.0.0", default-features = false }
|
||||
lettre = { version = "0.11.18", default-features = false, features = [
|
||||
json-patch = { version = "4.1.0", default-features = false }
|
||||
lettre = { version = "0.11.19", default-features = false, features = [
|
||||
"aws-lc-rs",
|
||||
"builder",
|
||||
"hostname",
|
||||
"pool",
|
||||
"ring",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"smtp-transport",
|
||||
@@ -101,37 +101,38 @@ lettre = { version = "0.11.18", default-features = false, features = [
|
||||
"tokio1-rustls",
|
||||
] }
|
||||
maxminddb = "0.26.0"
|
||||
meilisearch-sdk = { version = "0.29.1", default-features = false }
|
||||
meilisearch-sdk = { version = "0.30.0", default-features = false }
|
||||
murmur2 = "0.1.0"
|
||||
native-dialog = "0.9.0"
|
||||
native-dialog = "0.9.2"
|
||||
notify = { version = "8.2.0", default-features = false }
|
||||
notify-debouncer-mini = { version = "0.7.0", default-features = false }
|
||||
p256 = "0.13.2"
|
||||
paste = "1.0.15"
|
||||
path-util = { path = "packages/path-util" }
|
||||
phf = { version = "0.12.1", features = ["macros"] }
|
||||
png = "0.17.16"
|
||||
phf = { version = "0.13.1", features = ["macros"] }
|
||||
png = "0.18.0"
|
||||
prometheus = "0.14.0"
|
||||
quartz_nbt = "0.2.9"
|
||||
quick-xml = "0.38.1"
|
||||
quick-xml = "0.38.3"
|
||||
rand = "=0.8.5" # Locked on 0.8 until argon2 and p256 update to 0.9
|
||||
rand_chacha = "=0.3.1" # Locked on 0.3 until we can update rand to 0.9
|
||||
redis = "0.32.4"
|
||||
regex = "1.11.1"
|
||||
reqwest = { version = "0.12.22", default-features = false }
|
||||
redis = "0.32.7"
|
||||
regex = "1.12.2"
|
||||
reqwest = { version = "0.12.24", default-features = false }
|
||||
rgb = "0.8.52"
|
||||
rust_decimal = { version = "1.37.2", features = [
|
||||
rust_decimal = { version = "1.39.0", features = [
|
||||
"serde-with-float",
|
||||
"serde-with-str",
|
||||
] }
|
||||
rust_iso3166 = "0.1.14"
|
||||
rust-s3 = { version = "0.35.1", default-features = false, features = [
|
||||
rust-s3 = { version = "0.37.0", default-features = false, features = [
|
||||
"fail-on-err",
|
||||
"tags",
|
||||
"tokio-rustls-tls",
|
||||
] }
|
||||
rustls = "0.23.32"
|
||||
rusty-money = "0.4.1"
|
||||
sentry = { version = "0.42.0", default-features = false, features = [
|
||||
sentry = { version = "0.45.0", default-features = false, features = [
|
||||
"backtrace",
|
||||
"contexts",
|
||||
"debug-images",
|
||||
@@ -139,37 +140,37 @@ sentry = { version = "0.42.0", default-features = false, features = [
|
||||
"reqwest",
|
||||
"rustls",
|
||||
] }
|
||||
sentry-actix = "0.42.0"
|
||||
serde = "1.0.219"
|
||||
serde_bytes = "0.11.17"
|
||||
sentry-actix = "0.45.0"
|
||||
serde = "1.0.228"
|
||||
serde_bytes = "0.11.19"
|
||||
serde_cbor = "0.11.2"
|
||||
serde_ini = "0.2.0"
|
||||
serde_json = "1.0.142"
|
||||
serde_with = "3.14.0"
|
||||
serde_json = "1.0.145"
|
||||
serde_with = "3.15.0"
|
||||
serde-xml-rs = "0.8.1" # Also an XML (de)serializer, consider dropping yaserde in favor of this
|
||||
sha1 = "0.10.6"
|
||||
sha1_smol = { version = "1.0.1", features = ["std"] }
|
||||
sha2 = "0.10.9"
|
||||
spdx = "0.10.9"
|
||||
spdx = "0.12.0"
|
||||
sqlx = { version = "0.8.6", default-features = false }
|
||||
sysinfo = { version = "0.36.1", default-features = false }
|
||||
sysinfo = { version = "0.37.2", default-features = false }
|
||||
tar = "0.4.44"
|
||||
tauri = "2.7.0"
|
||||
tauri-build = "2.3.1"
|
||||
tauri-plugin-deep-link = "2.4.1"
|
||||
tauri-plugin-dialog = "2.3.2"
|
||||
tauri-plugin-http = "2.5.1"
|
||||
tauri-plugin-opener = "2.4.0"
|
||||
tauri-plugin-os = "2.3.0"
|
||||
tauri-plugin-single-instance = "2.3.2"
|
||||
tauri = "2.8.5"
|
||||
tauri-build = "2.4.1"
|
||||
tauri-plugin-deep-link = "2.4.3"
|
||||
tauri-plugin-dialog = "2.4.0"
|
||||
tauri-plugin-http = "2.5.2"
|
||||
tauri-plugin-opener = "2.5.0"
|
||||
tauri-plugin-os = "2.3.1"
|
||||
tauri-plugin-single-instance = "2.3.4"
|
||||
tauri-plugin-updater = { version = "2.9.0", default-features = false, features = [
|
||||
"rustls-tls",
|
||||
"zip",
|
||||
] }
|
||||
tauri-plugin-window-state = "2.4.0"
|
||||
tempfile = "3.20.0"
|
||||
tempfile = "3.23.0"
|
||||
theseus = { path = "packages/app-lib" }
|
||||
thiserror = "2.0.12"
|
||||
thiserror = "2.0.17"
|
||||
tikv-jemalloc-ctl = "0.6.0"
|
||||
tikv-jemallocator = "0.6.0"
|
||||
tokio = "1.47.1"
|
||||
@@ -180,22 +181,22 @@ tracing = "0.1.41"
|
||||
tracing-actix-web = { version = "0.7.19", default-features = false }
|
||||
tracing-ecs = "0.5.0"
|
||||
tracing-error = "0.2.1"
|
||||
tracing-subscriber = "0.3.19"
|
||||
typed-path = "0.11.0"
|
||||
url = "2.5.4"
|
||||
tracing-subscriber = "0.3.20"
|
||||
typed-path = "0.12.0"
|
||||
url = "2.5.7"
|
||||
urlencoding = "2.1.3"
|
||||
uuid = "1.17.0"
|
||||
uuid = "1.18.1"
|
||||
validator = "0.20.0"
|
||||
webp = { version = "0.3.0", default-features = false }
|
||||
webp = { version = "0.3.1", default-features = false }
|
||||
webview2-com = "0.38.0" # Should be updated in lockstep with wry
|
||||
whoami = "1.6.0"
|
||||
windows = "0.61.3"
|
||||
windows-core = "0.61.2"
|
||||
whoami = "1.6.1"
|
||||
windows = "=0.61.3" # Locked on 0.61 until we can update windows-core to 0.62
|
||||
windows-core = "=0.61.2" # Locked on 0.61 until webview2-com updates to 0.62
|
||||
winreg = "0.55.0"
|
||||
woothee = "0.13.0"
|
||||
yaserde = "0.12.0"
|
||||
zbus = "5.9.0"
|
||||
zip = { version = "4.3.0", default-features = false, features = [
|
||||
zbus = "5.11.0"
|
||||
zip = { version = "6.0.0", default-features = false, features = [
|
||||
"bzip2",
|
||||
"deflate",
|
||||
"deflate64",
|
||||
@@ -234,6 +235,7 @@ redundant_clone = "warn"
|
||||
redundant_feature_names = "warn"
|
||||
redundant_type_annotations = "warn"
|
||||
todo = "warn"
|
||||
uninlined_format_args = "warn"
|
||||
unnested_or_patterns = "warn"
|
||||
wildcard_dependencies = "warn"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM rust:1.89.0 AS build
|
||||
FROM rust:1.90.0 AS build
|
||||
|
||||
WORKDIR /usr/src/daedalus
|
||||
COPY . .
|
||||
|
||||
@@ -5,7 +5,7 @@ description: Guide for contributing to Modrinth's backend
|
||||
|
||||
This project is part of our [monorepo](https://github.com/modrinth/code). You can find it in the `apps/labrinth` directory. The instructions below assume that you have switched your working directory to the `apps/labrinth` subdirectory.
|
||||
|
||||
[labrinth] is the Rust-based backend serving Modrinth's API with the help of the [Actix](https://actix.rs) framework. To get started with a labrinth instance, install docker, docker-compose (which comes with Docker), and [Rust]. The initial startup can be done simply with the command `docker-compose up`, or with `docker compose up` (Compose V2 and later). That will deploy a PostgreSQL database on port 5432, a MeiliSearch instance on port 7700, and a [Mailpit](https://mailpit.axllent.org/) SMTP server on port 1025, with a web UI to inspect sent emails on port 8025. To run the API itself, you'll need to use the `cargo run` command, this will deploy the API on port 8000.
|
||||
[labrinth] is the Rust-based backend serving Modrinth's API with the help of the [Actix](https://actix.rs) framework. To get started with a labrinth instance, install docker, docker-compose (which comes with Docker), cmake, and [Rust]. The initial startup can be done simply with the command `docker-compose up`, or with `docker compose up` (Compose V2 and later). That will deploy a PostgreSQL database on port 5432, a MeiliSearch instance on port 7700, and a [Mailpit](https://mailpit.axllent.org/) SMTP server on port 1025, with a web UI to inspect sent emails on port 8025. To run the API itself, you'll need to use the `cargo run` command, this will deploy the API on port 8000.
|
||||
|
||||
To get a basic configuration, copy the `.env.local` file to `.env`. Now, you'll have to install the sqlx CLI, which can be done with cargo:
|
||||
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
::backdrop,
|
||||
:root[data-theme='light'],
|
||||
[data-theme='light'] ::backdrop {
|
||||
--sl-font-system:
|
||||
Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Roboto, Cantarell,
|
||||
Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
--sl-font-system: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Roboto,
|
||||
Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
|
||||
--sl-color-white: var(--color-contrast); /* “white” */
|
||||
--sl-color-gray-1: var(--color-base);
|
||||
|
||||
@@ -113,6 +113,10 @@ migrations/20250725230041_reports-closed-status-index.sql
|
||||
migrations/20250727184120_user-newsletter-subscription-column.sql
|
||||
migrations/20250804221014_users-redeemals.sql
|
||||
migrations/20250805001654_product-prices-public.sql
|
||||
migrations/20250823233518_user-compliance.sql
|
||||
migrations/20250902133943_notification-extension.sql
|
||||
migrations/20250914190749_affiliate_codes.sql
|
||||
migrations/20250927120742_user_limits.sql
|
||||
|
||||
# Prettier reformats some of the PostgreSQL-specific COPY syntax here,
|
||||
# which is very likely to break things
|
||||
|
||||
@@ -91,6 +91,7 @@ rust_decimal = { workspace = true, features = [
|
||||
] }
|
||||
rust_iso3166 = { workspace = true }
|
||||
rust-s3 = { workspace = true }
|
||||
rustls.workspace = true
|
||||
rusty-money = { workspace = true }
|
||||
sentry = { workspace = true }
|
||||
sentry-actix = { workspace = true }
|
||||
@@ -108,7 +109,7 @@ sqlx = { workspace = true, features = [
|
||||
"postgres",
|
||||
"runtime-tokio",
|
||||
"rust_decimal",
|
||||
"tls-rustls-ring",
|
||||
"tls-rustls-aws-lc-rs",
|
||||
] }
|
||||
tar = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM rust:1.89.0 AS build
|
||||
FROM rust:1.90.0 AS build
|
||||
|
||||
WORKDIR /usr/src/labrinth
|
||||
COPY . .
|
||||
|
||||
@@ -113,6 +113,10 @@ async fn main() -> std::io::Result<()> {
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
rustls::crypto::aws_lc_rs::default_provider()
|
||||
.install_default()
|
||||
.unwrap();
|
||||
|
||||
// DSN is from SENTRY_DSN env variable.
|
||||
// Has no effect if not set.
|
||||
let sentry = sentry::init(sentry::ClientOptions {
|
||||
|
||||
@@ -66,7 +66,7 @@ impl AnalyticsQueue {
|
||||
self.playtime_queue.clear();
|
||||
|
||||
if !playtime_queue.is_empty() {
|
||||
let mut playtimes = client.insert("playtime")?;
|
||||
let mut playtimes = client.insert::<Playtime>("playtime").await?;
|
||||
|
||||
for playtime in playtime_queue {
|
||||
playtimes.write(&playtime).await?;
|
||||
@@ -132,7 +132,7 @@ impl AnalyticsQueue {
|
||||
.await
|
||||
.map_err(DatabaseError::CacheError)?;
|
||||
|
||||
let mut views = client.insert("views")?;
|
||||
let mut views = client.insert::<PageView>("views").await?;
|
||||
|
||||
for (all_views, monetized) in raw_views {
|
||||
for (idx, mut view) in all_views.into_iter().enumerate() {
|
||||
@@ -200,7 +200,7 @@ impl AnalyticsQueue {
|
||||
.map_err(DatabaseError::CacheError)?;
|
||||
|
||||
let mut transaction = pool.begin().await?;
|
||||
let mut downloads = client.insert("downloads")?;
|
||||
let mut downloads = client.insert::<Download>("downloads").await?;
|
||||
|
||||
let mut version_downloads: HashMap<i64, i32> = HashMap::new();
|
||||
let mut project_downloads: HashMap<i64, i32> = HashMap::new();
|
||||
|
||||
@@ -704,7 +704,7 @@ async fn dynamic_email_body(
|
||||
.wrap_internal_err("SITE_URL is not set")?;
|
||||
let site_url = site_url.trim_end_matches('/');
|
||||
|
||||
let url = format!("{}/_internal/templates/email/dynamic", site_url);
|
||||
let url = format!("{site_url}/_internal/templates/email/dynamic");
|
||||
|
||||
std::str::from_utf8(
|
||||
reqwest::Client::new()
|
||||
|
||||
@@ -686,11 +686,12 @@ async fn query_clickhouse<Row>(
|
||||
cx: &mut QueryClickhouseContext<'_>,
|
||||
query: &str,
|
||||
use_columns: &[(&str, bool)],
|
||||
row_get_bucket: impl Fn(&Row) -> u64,
|
||||
row_to_analytics: impl Fn(Row) -> AnalyticsData,
|
||||
// I hate using the hidden type Row::Value here, but it's what next() returns, so I see no other option
|
||||
row_get_bucket: impl Fn(&Row::Value<'_>) -> u64,
|
||||
row_to_analytics: impl Fn(Row::Value<'_>) -> AnalyticsData,
|
||||
) -> Result<(), ApiError>
|
||||
where
|
||||
Row: clickhouse::Row + serde::de::DeserializeOwned + std::fmt::Debug,
|
||||
Row: clickhouse::RowRead + serde::de::DeserializeOwned + std::fmt::Debug,
|
||||
{
|
||||
let mut query = cx
|
||||
.clickhouse
|
||||
|
||||
@@ -127,6 +127,7 @@ pub async fn swap_index(
|
||||
let index_name = config.get_index_name(index_name, false);
|
||||
let swap_indices = SwapIndexes {
|
||||
indexes: (index_name_next, index_name),
|
||||
rename: None,
|
||||
};
|
||||
client
|
||||
.swap_indexes([&swap_indices])
|
||||
|
||||
@@ -18,7 +18,7 @@ impl super::Validator for DataPackValidator {
|
||||
fn get_supported_game_versions(&self) -> SupportedGameVersions {
|
||||
// Time since release of 17w43a, 2017-10-25, which introduced datapacks
|
||||
SupportedGameVersions::PastDate(
|
||||
DateTime::from_timestamp(1508889600, 0).unwrap(),
|
||||
DateTime::from_timestamp_secs(1508889600).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ impl super::Validator for ForgeValidator {
|
||||
fn get_supported_game_versions(&self) -> SupportedGameVersions {
|
||||
// Time since release of 1.13, the first forge version which uses the new TOML system
|
||||
SupportedGameVersions::PastDate(
|
||||
DateTime::from_timestamp(1540122067, 0).unwrap(),
|
||||
DateTime::from_timestamp_secs(1540122067).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -56,8 +56,8 @@ impl super::Validator for LegacyForgeValidator {
|
||||
fn get_supported_game_versions(&self) -> SupportedGameVersions {
|
||||
// Times between versions 1.5.2 to 1.12.2, which all use the legacy way of defining mods
|
||||
SupportedGameVersions::Range(
|
||||
DateTime::from_timestamp(0, 0).unwrap(),
|
||||
DateTime::from_timestamp(1540122066, 0).unwrap(),
|
||||
DateTime::from_timestamp_secs(0).unwrap(),
|
||||
DateTime::from_timestamp_secs(1540122066).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ impl super::Validator for QuiltValidator {
|
||||
|
||||
fn get_supported_game_versions(&self) -> SupportedGameVersions {
|
||||
SupportedGameVersions::PastDate(
|
||||
DateTime::from_timestamp(1646070100, 0).unwrap(),
|
||||
DateTime::from_timestamp_secs(1646070100).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ impl super::Validator for PackValidator {
|
||||
fn get_supported_game_versions(&self) -> SupportedGameVersions {
|
||||
// Time since release of 13w24a which replaced texture packs with resource packs
|
||||
SupportedGameVersions::PastDate(
|
||||
DateTime::from_timestamp(1371137542, 0).unwrap(),
|
||||
DateTime::from_timestamp_secs(1371137542).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ impl super::Validator for TexturePackValidator {
|
||||
fn get_supported_game_versions(&self) -> SupportedGameVersions {
|
||||
// a1.2.2a to 13w23b
|
||||
SupportedGameVersions::Range(
|
||||
DateTime::from_timestamp(1289339999, 0).unwrap(),
|
||||
DateTime::from_timestamp(1370651522, 0).unwrap(),
|
||||
DateTime::from_timestamp_secs(1289339999).unwrap(),
|
||||
DateTime::from_timestamp_secs(1370651522).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ pub async fn setup(db: &database::TemporaryDatabase) -> LabrinthConfig {
|
||||
println!("Some environment variables are missing!");
|
||||
}
|
||||
|
||||
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
||||
|
||||
let pool = db.pool.clone();
|
||||
let ro_pool = db.ro_pool.clone();
|
||||
let redis_pool = db.redis_pool.clone();
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
allow-dbg-in-tests = true
|
||||
msrv = "1.89.0"
|
||||
msrv = "1.90.0"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
**/*.rs
|
||||
.sqlx
|
||||
|
||||
java/build
|
||||
|
||||
# Migrations existing before Prettier formatted them shall always be ignored,
|
||||
# as any changes to them will break existing deployments
|
||||
migrations/20240711194701_init.sql
|
||||
|
||||
@@ -1,18 +1,7 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction
|
||||
import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransformer
|
||||
import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer
|
||||
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
|
||||
import org.apache.tools.zip.ZipEntry
|
||||
import org.apache.tools.zip.ZipOutputStream
|
||||
import java.io.IOException
|
||||
import java.util.jar.JarFile
|
||||
import java.util.jar.Attributes as JarAttributes
|
||||
import java.util.jar.Manifest as JarManifest
|
||||
|
||||
plugins {
|
||||
java
|
||||
id("com.diffplug.spotless") version "7.0.4"
|
||||
id("com.gradleup.shadow") version "9.0.0-rc2"
|
||||
id("com.diffplug.spotless") version "8.0.0"
|
||||
id("com.gradleup.shadow") version "9.2.2"
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -20,9 +9,9 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.ow2.asm:asm:9.8")
|
||||
implementation("org.ow2.asm:asm-tree:9.8")
|
||||
implementation("com.google.code.gson:gson:2.13.1")
|
||||
implementation("org.ow2.asm:asm:9.9")
|
||||
implementation("org.ow2.asm:asm-tree:9.9")
|
||||
implementation("com.google.code.gson:gson:2.13.2")
|
||||
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
@@ -30,7 +19,7 @@ dependencies {
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(11)
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,52 +45,9 @@ tasks.shadowJar {
|
||||
attributes["Premain-Class"] = "com.modrinth.theseus.agent.TheseusAgent"
|
||||
}
|
||||
|
||||
enableRelocation = true
|
||||
addMultiReleaseAttribute = false
|
||||
enableAutoRelocation = true
|
||||
relocationPrefix = "com.modrinth.theseus.shadow"
|
||||
|
||||
// Adapted from ManifestResourceTransformer to do one thing: remove Multi-Release.
|
||||
// Multi-Release gets added by shadow because gson has Multi-Release set to true, however
|
||||
// shadow strips the actual versions directory, as gson only has a module-info.class in there.
|
||||
// However, older versions of SecureJarHandler crash if Multi-Release is set to true but the
|
||||
// versions directory is missing.
|
||||
transform(@CacheableTransformer object : ResourceTransformer {
|
||||
private var manifestDiscovered = false
|
||||
private var manifest: JarManifest? = null
|
||||
|
||||
override fun canTransformResource(element: FileTreeElement): Boolean {
|
||||
return JarFile.MANIFEST_NAME.equals(element.path, ignoreCase = true)
|
||||
}
|
||||
|
||||
override fun transform(context: TransformerContext) {
|
||||
if (!manifestDiscovered) {
|
||||
try {
|
||||
manifest = JarManifest(context.inputStream)
|
||||
manifestDiscovered = true
|
||||
} catch (e: IOException) {
|
||||
logger.warn("Failed to read MANIFEST.MF", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasTransformedResource(): Boolean = true
|
||||
|
||||
override fun modifyOutputStream(
|
||||
os: ZipOutputStream,
|
||||
preserveFileTimestamps: Boolean
|
||||
) {
|
||||
// If we didn't find a manifest, then let's create one.
|
||||
if (manifest == null) {
|
||||
manifest = JarManifest()
|
||||
}
|
||||
|
||||
manifest!!.mainAttributes.remove(JarAttributes.Name.MULTI_RELEASE)
|
||||
|
||||
os.putNextEntry(ZipEntry(JarFile.MANIFEST_NAME).apply {
|
||||
time = ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES
|
||||
})
|
||||
manifest!!.write(os)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
tasks.named<Test>("test") {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[versions]
|
||||
junit-jupiter = "5.12.1"
|
||||
junit-jupiter = "5.14.0"
|
||||
|
||||
[libraries]
|
||||
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
// Apply the foojay-resolver plugin to allow automatic download of JDKs
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0"
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
|
||||
}
|
||||
|
||||
rootProject.name = "theseus"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Miscellaneous PNG utilities for Minecraft skins.
|
||||
|
||||
use std::io::Read;
|
||||
use std::io::{BufRead, Cursor, Seek};
|
||||
use std::sync::Arc;
|
||||
|
||||
use base64::Engine;
|
||||
@@ -9,7 +9,8 @@ use data_url::DataUrl;
|
||||
use futures::{Stream, TryStreamExt, future::Either, stream};
|
||||
use itertools::Itertools;
|
||||
use rgb::Rgba;
|
||||
use tokio_util::{compat::FuturesAsyncReadCompatExt, io::SyncIoBridge};
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio_util::compat::FuturesAsyncReadCompatExt;
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
@@ -95,7 +96,8 @@ pub fn dimensions(png_data: &[u8]) -> crate::Result<(u32, u32)> {
|
||||
pub async fn normalize_skin_texture(
|
||||
texture: &UrlOrBlob,
|
||||
) -> crate::Result<Bytes> {
|
||||
let texture_stream = SyncIoBridge::new(Box::pin(
|
||||
let mut texture_data = Vec::with_capacity(8192);
|
||||
Box::pin(
|
||||
match texture {
|
||||
UrlOrBlob::Url(url) => Either::Left(
|
||||
url_to_data_stream(url)
|
||||
@@ -112,84 +114,84 @@ pub async fn normalize_skin_texture(
|
||||
),
|
||||
}
|
||||
.compat(),
|
||||
));
|
||||
)
|
||||
.read_to_end(&mut texture_data)
|
||||
.await?;
|
||||
|
||||
tokio::task::spawn_blocking(|| {
|
||||
let mut png_reader = {
|
||||
let mut decoder = png::Decoder::new(texture_stream);
|
||||
decoder.set_transformations(
|
||||
png::Transformations::normalize_to_color8(),
|
||||
);
|
||||
decoder.read_info()
|
||||
}?;
|
||||
let mut png_reader = {
|
||||
let mut decoder = png::Decoder::new(Cursor::new(texture_data));
|
||||
decoder
|
||||
.set_transformations(png::Transformations::normalize_to_color8());
|
||||
decoder.read_info()
|
||||
}?;
|
||||
|
||||
// The code below assumes that the skin texture has valid dimensions.
|
||||
// This also serves as a way to bail out early for obviously invalid or
|
||||
// adversarial textures
|
||||
if png_reader.info().width != 64
|
||||
|| ![64, 32].contains(&png_reader.info().height)
|
||||
{
|
||||
Err(ErrorKind::InvalidSkinTexture)?;
|
||||
}
|
||||
// The code below assumes that the skin texture has valid dimensions.
|
||||
// This also serves as a way to bail out early for obviously invalid or
|
||||
// adversarial textures
|
||||
if png_reader.info().width != 64
|
||||
|| ![64, 32].contains(&png_reader.info().height)
|
||||
{
|
||||
Err(ErrorKind::InvalidSkinTexture)?;
|
||||
}
|
||||
|
||||
let is_legacy_skin = png_reader.info().height == 32;
|
||||
let mut texture_buf =
|
||||
get_skin_texture_buffer(&mut png_reader, is_legacy_skin)?;
|
||||
if is_legacy_skin {
|
||||
convert_legacy_skin_texture(&mut texture_buf, png_reader.info());
|
||||
do_notch_transparency_hack(&mut texture_buf, png_reader.info());
|
||||
}
|
||||
make_inner_parts_opaque(&mut texture_buf, png_reader.info());
|
||||
let is_legacy_skin = png_reader.info().height == 32;
|
||||
let mut texture_buf =
|
||||
get_skin_texture_buffer(&mut png_reader, is_legacy_skin)?;
|
||||
if is_legacy_skin {
|
||||
convert_legacy_skin_texture(&mut texture_buf, png_reader.info());
|
||||
do_notch_transparency_hack(&mut texture_buf, png_reader.info());
|
||||
}
|
||||
make_inner_parts_opaque(&mut texture_buf, png_reader.info());
|
||||
|
||||
let mut encoded_png = vec![];
|
||||
let mut encoded_png = vec![];
|
||||
|
||||
let mut png_encoder = png::Encoder::new(&mut encoded_png, 64, 64);
|
||||
png_encoder.set_color(png::ColorType::Rgba);
|
||||
png_encoder.set_depth(png::BitDepth::Eight);
|
||||
png_encoder.set_filter(png::FilterType::NoFilter);
|
||||
png_encoder.set_compression(png::Compression::Fast);
|
||||
let mut png_encoder = png::Encoder::new(&mut encoded_png, 64, 64);
|
||||
png_encoder.set_color(png::ColorType::Rgba);
|
||||
png_encoder.set_depth(png::BitDepth::Eight);
|
||||
png_encoder.set_filter(png::Filter::NoFilter);
|
||||
png_encoder.set_compression(png::Compression::Fast);
|
||||
|
||||
// Keeping color space information properly set, to handle the occasional
|
||||
// strange PNG with non-sRGB chromaticities and/or different grayscale spaces
|
||||
// that keeps most people wondering, is what sets a carefully crafted image
|
||||
// manipulation routine apart :)
|
||||
if let Some(source_chromaticities) =
|
||||
png_reader.info().source_chromaticities.as_ref().copied()
|
||||
{
|
||||
png_encoder.set_source_chromaticities(source_chromaticities);
|
||||
}
|
||||
if let Some(source_gamma) =
|
||||
png_reader.info().source_gamma.as_ref().copied()
|
||||
{
|
||||
png_encoder.set_source_gamma(source_gamma);
|
||||
}
|
||||
if let Some(source_srgb) = png_reader.info().srgb.as_ref().copied() {
|
||||
png_encoder.set_source_srgb(source_srgb);
|
||||
}
|
||||
// Keeping color space information properly set, to handle the occasional
|
||||
// strange PNG with non-sRGB chromaticities and/or different grayscale spaces
|
||||
// that keeps most people wondering, is what sets a carefully crafted image
|
||||
// manipulation routine apart :)
|
||||
if let Some(source_chromaticities) =
|
||||
png_reader.info().source_chromaticities.as_ref().copied()
|
||||
{
|
||||
png_encoder.set_source_chromaticities(source_chromaticities);
|
||||
}
|
||||
if let Some(source_gamma) = png_reader.info().source_gamma.as_ref().copied()
|
||||
{
|
||||
png_encoder.set_source_gamma(source_gamma);
|
||||
}
|
||||
if let Some(source_srgb) = png_reader.info().srgb.as_ref().copied() {
|
||||
png_encoder.set_source_srgb(source_srgb);
|
||||
}
|
||||
|
||||
let png_buf = bytemuck::try_cast_slice(&texture_buf)
|
||||
.map_err(|_| ErrorKind::InvalidPng)?;
|
||||
let mut png_writer = png_encoder.write_header()?;
|
||||
png_writer.write_image_data(png_buf)?;
|
||||
png_writer.finish()?;
|
||||
let png_buf = bytemuck::try_cast_slice(&texture_buf)
|
||||
.map_err(|_| ErrorKind::InvalidPng)?;
|
||||
let mut png_writer = png_encoder.write_header()?;
|
||||
png_writer.write_image_data(png_buf)?;
|
||||
png_writer.finish()?;
|
||||
|
||||
Ok(encoded_png.into())
|
||||
})
|
||||
.await?
|
||||
Ok(encoded_png.into())
|
||||
}
|
||||
|
||||
/// Reads a skin texture and returns a 64x64 buffer in RGBA format.
|
||||
fn get_skin_texture_buffer<R: Read>(
|
||||
fn get_skin_texture_buffer<R: BufRead + Seek>(
|
||||
png_reader: &mut png::Reader<R>,
|
||||
is_legacy_skin: bool,
|
||||
) -> crate::Result<Vec<Rgba<u8>>> {
|
||||
let output_buffer_size = png_reader
|
||||
.output_buffer_size()
|
||||
.expect("Reasonable skin texture size verified already");
|
||||
let mut png_buf = if is_legacy_skin {
|
||||
// Legacy skins have half the height, so duplicate the rows to
|
||||
// turn them into a 64x64 texture
|
||||
vec![0; png_reader.output_buffer_size() * 2]
|
||||
vec![0; output_buffer_size * 2]
|
||||
} else {
|
||||
// Modern skins are left as-is
|
||||
vec![0; png_reader.output_buffer_size()]
|
||||
vec![0; output_buffer_size]
|
||||
};
|
||||
png_reader.next_frame(&mut png_buf)?;
|
||||
|
||||
@@ -373,9 +375,10 @@ fn set_alpha(
|
||||
#[tokio::test]
|
||||
async fn normalize_skin_texture_works() {
|
||||
let decode_to_pixels = |png_data: &[u8]| {
|
||||
let decoder = png::Decoder::new(png_data);
|
||||
let decoder = png::Decoder::new(Cursor::new(png_data));
|
||||
let mut reader = decoder.read_info().expect("Failed to read PNG info");
|
||||
let mut buffer = vec![0; reader.output_buffer_size()];
|
||||
let mut buffer =
|
||||
vec![0; reader.output_buffer_size().expect("Skin size too large")];
|
||||
reader
|
||||
.next_frame(&mut buffer)
|
||||
.expect("Failed to decode PNG");
|
||||
|
||||
@@ -176,6 +176,9 @@ pub enum ErrorKind {
|
||||
|
||||
#[error("Deserialization error: {0}")]
|
||||
DeserializationError(#[from] serde::de::value::Error),
|
||||
|
||||
#[error("Discord IPC error: {0}")]
|
||||
DiscordRichPresenceError(#[from] discord_rich_presence::error::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -18,12 +18,7 @@ impl DiscordGuard {
|
||||
/// Initialize discord IPC client, and attempt to connect to it
|
||||
/// If it fails, it will still return a DiscordGuard, but the client will be unconnected
|
||||
pub fn init() -> crate::Result<DiscordGuard> {
|
||||
let dipc =
|
||||
DiscordIpcClient::new("1123683254248148992").map_err(|e| {
|
||||
crate::ErrorKind::OtherError(format!(
|
||||
"Could not create Discord client {e}",
|
||||
))
|
||||
})?;
|
||||
let dipc = DiscordIpcClient::new("1123683254248148992");
|
||||
|
||||
Ok(DiscordGuard {
|
||||
client: Arc::new(RwLock::new(dipc)),
|
||||
@@ -87,25 +82,14 @@ impl DiscordGuard {
|
||||
let mut client: tokio::sync::RwLockWriteGuard<'_, DiscordIpcClient> =
|
||||
self.client.write().await;
|
||||
let res = client.set_activity(activity.clone());
|
||||
let could_not_set_err = |e: Box<dyn serde::ser::StdError>| {
|
||||
crate::ErrorKind::OtherError(format!(
|
||||
"Could not update Discord activity {e}",
|
||||
))
|
||||
};
|
||||
|
||||
if reconnect_if_fail {
|
||||
if let Err(_e) = res {
|
||||
client.reconnect().map_err(|e| {
|
||||
crate::ErrorKind::OtherError(format!(
|
||||
"Could not reconnect to Discord IPC {e}",
|
||||
))
|
||||
})?;
|
||||
return Ok(client
|
||||
.set_activity(activity)
|
||||
.map_err(could_not_set_err)?); // try again, but don't reconnect if it fails again
|
||||
client.reconnect()?;
|
||||
return Ok(client.set_activity(activity)?); // try again, but don't reconnect if it fails again
|
||||
}
|
||||
} else {
|
||||
res.map_err(could_not_set_err)?;
|
||||
res?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -126,25 +110,13 @@ impl DiscordGuard {
|
||||
let mut client = self.client.write().await;
|
||||
let res = client.clear_activity();
|
||||
|
||||
let could_not_clear_err = |e: Box<dyn serde::ser::StdError>| {
|
||||
crate::ErrorKind::OtherError(format!(
|
||||
"Could not clear Discord activity {e}",
|
||||
))
|
||||
};
|
||||
|
||||
if reconnect_if_fail {
|
||||
if res.is_err() {
|
||||
client.reconnect().map_err(|e| {
|
||||
crate::ErrorKind::OtherError(format!(
|
||||
"Could not reconnect to Discord IPC {e}",
|
||||
))
|
||||
})?;
|
||||
return Ok(client
|
||||
.clear_activity()
|
||||
.map_err(could_not_clear_err)?); // try again, but don't reconnect if it fails again
|
||||
client.reconnect()?;
|
||||
return Ok(client.clear_activity()?); // try again, but don't reconnect if it fails again
|
||||
}
|
||||
} else {
|
||||
res.map_err(could_not_clear_err)?;
|
||||
res?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ impl FriendsSocket {
|
||||
pub async fn disconnect(&self) -> crate::Result<()> {
|
||||
let mut write_lock = self.write.write().await;
|
||||
if let Some(ref mut write_half) = *write_lock {
|
||||
write_half.close().await?;
|
||||
SinkExt::close(write_half).await?;
|
||||
*write_lock = None;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -516,7 +516,7 @@ impl Process {
|
||||
chrono::DateTime::<Utc>::from_timestamp(secs, nsecs)
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
chrono::DateTime::<Utc>::from_timestamp(timestamp_val, 0)
|
||||
chrono::DateTime::<Utc>::from_timestamp_secs(timestamp_val)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
import { article as a_new_chapter_for_modrinth_servers } from "./a_new_chapter_for_modrinth_servers";
|
||||
import { article as accelerating_development } from "./accelerating_development";
|
||||
import { article as becoming_sustainable } from "./becoming_sustainable";
|
||||
import { article as capital_return } from "./capital_return";
|
||||
import { article as carbon_ads } from "./carbon_ads";
|
||||
import { article as creator_monetization } from "./creator_monetization";
|
||||
import { article as creator_update } from "./creator_update";
|
||||
import { article as creator_updates_july_2025 } from "./creator_updates_july_2025";
|
||||
import { article as design_refresh } from "./design_refresh";
|
||||
import { article as download_adjustment } from "./download_adjustment";
|
||||
import { article as free_server_medal } from "./free_server_medal";
|
||||
import { article as knossos_v2_1_0 } from "./knossos_v2_1_0";
|
||||
import { article as licensing_guide } from "./licensing_guide";
|
||||
import { article as modpack_changes } from "./modpack_changes";
|
||||
import { article as modpacks_alpha } from "./modpacks_alpha";
|
||||
import { article as modrinth_app_beta } from "./modrinth_app_beta";
|
||||
import { article as modrinth_beta } from "./modrinth_beta";
|
||||
import { article as modrinth_servers_asia } from "./modrinth_servers_asia";
|
||||
import { article as modrinth_servers_beta } from "./modrinth_servers_beta";
|
||||
import { article as new_environments } from "./new_environments";
|
||||
import { article as new_site_beta } from "./new_site_beta";
|
||||
import { article as plugins_resource_packs } from "./plugins_resource_packs";
|
||||
import { article as pride_campaign_2025 } from "./pride_campaign_2025";
|
||||
import { article as redesign } from "./redesign";
|
||||
import { article as russian_censorship } from "./russian_censorship";
|
||||
import { article as skins_now_in_modrinth_app } from "./skins_now_in_modrinth_app";
|
||||
import { article as standing_by_our_values } from "./standing_by_our_values";
|
||||
import { article as standing_by_our_values_russian } from "./standing_by_our_values_russian";
|
||||
import { article as windows_borderless_malware_disclosure } from "./windows_borderless_malware_disclosure";
|
||||
import { article as whats_modrinth } from "./whats_modrinth";
|
||||
import { article as two_years_of_modrinth } from "./two_years_of_modrinth";
|
||||
import { article as two_years_of_modrinth_history } from "./two_years_of_modrinth_history";
|
||||
import { article as whats_modrinth } from "./whats_modrinth";
|
||||
import { article as windows_borderless_malware_disclosure } from "./windows_borderless_malware_disclosure";
|
||||
import { article as standing_by_our_values } from "./standing_by_our_values";
|
||||
import { article as standing_by_our_values_russian } from "./standing_by_our_values_russian";
|
||||
import { article as skins_now_in_modrinth_app } from "./skins_now_in_modrinth_app";
|
||||
import { article as russian_censorship } from "./russian_censorship";
|
||||
import { article as redesign } from "./redesign";
|
||||
import { article as pride_campaign_2025 } from "./pride_campaign_2025";
|
||||
import { article as plugins_resource_packs } from "./plugins_resource_packs";
|
||||
import { article as new_site_beta } from "./new_site_beta";
|
||||
import { article as new_environments } from "./new_environments";
|
||||
import { article as modrinth_servers_beta } from "./modrinth_servers_beta";
|
||||
import { article as modrinth_servers_asia } from "./modrinth_servers_asia";
|
||||
import { article as modrinth_beta } from "./modrinth_beta";
|
||||
import { article as modrinth_app_beta } from "./modrinth_app_beta";
|
||||
import { article as modpacks_alpha } from "./modpacks_alpha";
|
||||
import { article as modpack_changes } from "./modpack_changes";
|
||||
import { article as licensing_guide } from "./licensing_guide";
|
||||
import { article as knossos_v2_1_0 } from "./knossos_v2_1_0";
|
||||
import { article as free_server_medal } from "./free_server_medal";
|
||||
import { article as download_adjustment } from "./download_adjustment";
|
||||
import { article as design_refresh } from "./design_refresh";
|
||||
import { article as creator_updates_july_2025 } from "./creator_updates_july_2025";
|
||||
import { article as creator_update } from "./creator_update";
|
||||
import { article as creator_monetization } from "./creator_monetization";
|
||||
import { article as carbon_ads } from "./carbon_ads";
|
||||
import { article as capital_return } from "./capital_return";
|
||||
import { article as becoming_sustainable } from "./becoming_sustainable";
|
||||
import { article as accelerating_development } from "./accelerating_development";
|
||||
import { article as a_new_chapter_for_modrinth_servers } from "./a_new_chapter_for_modrinth_servers";
|
||||
|
||||
export const articles = [
|
||||
windows_borderless_malware_disclosure,
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "1.89.0"
|
||||
channel = "1.90.0"
|
||||
profile = "default"
|
||||
|
||||
Reference in New Issue
Block a user