Taplo and typos in CI, TOML cleanup (#4510)

* Taplo and typos in CI

* Clean up Cargo.toml files

* Fix CI

* Fix CI

* Run typos in CI

* Loosen typos a bit

* Fix typos

* Fix taplo

* Switch to Tombi

* Fix Tombi errors

* Remove unused typos config

* Tombi fmt

* Remove extraneous cargo fmt

* fix typos
This commit is contained in:
aecsocket
2025-10-12 21:18:38 +01:00
committed by GitHub
parent ea594ec27c
commit 4cd8ccd319
46 changed files with 650 additions and 468 deletions

24
.github/workflows/check-generic.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
on:
pull_request:
push:
branches:
- master
env:
CARGO_TERM_COLOR: always
SQLX_OFFLINE: true
jobs:
typos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: crate-ci/typos@master
tombi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: tombi-toml/setup-tombi@v1
- run: tombi lint
- run: tombi fmt --check

19
.github/workflows/check-rust.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
on:
pull_request:
push:
branches:
- master
env:
CARGO_TERM_COLOR: always
SQLX_OFFLINE: true
jobs:
shear:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: cargo-bins/cargo-binstall@main
- run: cargo binstall --no-confirm cargo-shear
- run: cargo shear

View File

@@ -1,14 +1,14 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = [ members = [
"apps/app", "apps/app",
"apps/app-playground", "apps/app-playground",
"apps/daedalus_client", "apps/daedalus_client",
"apps/labrinth", "apps/labrinth",
"packages/app-lib", "packages/app-lib",
"packages/ariadne", "packages/ariadne",
"packages/daedalus", "packages/daedalus",
"packages/path-util", "packages/path-util",
] ]
[workspace.package] [workspace.package]
@@ -25,30 +25,34 @@ actix-web-prom = "0.10.0"
actix-ws = "0.3.0" actix-ws = "0.3.0"
argon2 = { version = "0.5.3", features = ["std"] } argon2 = { version = "0.5.3", features = ["std"] }
ariadne = { path = "packages/ariadne" } ariadne = { path = "packages/ariadne" }
async_zip = "0.0.17"
async-compression = { version = "0.4.27", default-features = false } async-compression = { version = "0.4.27", default-features = false }
async-recursion = "1.1.1" async-recursion = "1.1.1"
async-stripe = { version = "0.41.0", default-features = false, features = [ async-stripe = { version = "0.41.0", default-features = false, features = [
"runtime-tokio-hyper-rustls", "runtime-tokio-hyper-rustls",
] } ] }
async-trait = "0.1.88" async-trait = "0.1.88"
async-tungstenite = { version = "0.30.0", default-features = false, features = ["futures-03-sink"] } async-tungstenite = { version = "0.30.0", default-features = false, features = [
"futures-03-sink",
] }
async-walkdir = "2.1.0" async-walkdir = "2.1.0"
async_zip = "0.0.17"
base64 = "0.22.1" base64 = "0.22.1"
bitflags = "2.9.1" bitflags = "2.9.1"
bytemuck = "1.23.1" bytemuck = "1.23.1"
bytes = "1.10.1" bytes = "1.10.1"
typed-path = "0.11.0"
censor = "0.3.0" censor = "0.3.0"
chardetng = "0.1.17" chardetng = "0.1.17"
chrono = "0.4.41" chrono = "0.4.41"
cidre = { version = "0.11.2", default-features = false, features = ["macos_15_0"] } cidre = { version = "0.11.2", default-features = false, features = [
"macos_15_0",
] }
clap = "4.5.43" clap = "4.5.43"
clickhouse = "0.13.3" clickhouse = "0.13.3"
color-eyre = "0.6.5"
color-thief = "0.2.2" color-thief = "0.2.2"
console-subscriber = "0.4.1" console-subscriber = "0.4.1"
const_format = "0.2.34"
daedalus = { path = "packages/daedalus" } daedalus = { path = "packages/daedalus" }
path-util = { path = "packages/path-util" }
dashmap = "6.1.0" dashmap = "6.1.0"
data-url = "0.3.1" data-url = "0.3.1"
deadpool-redis = "0.22.0" deadpool-redis = "0.22.0"
@@ -61,6 +65,7 @@ dunce = "1.0.5"
either = "1.15.0" either = "1.15.0"
encoding_rs = "0.8.35" encoding_rs = "0.8.35"
enumset = "1.1.7" enumset = "1.1.7"
eyre = "0.6.12"
flate2 = "1.1.2" flate2 = "1.1.2"
fs4 = { version = "0.13.1", default-features = false } fs4 = { version = "0.13.1", default-features = false }
futures = { version = "0.3.31", default-features = false } futures = { version = "0.3.31", default-features = false }
@@ -70,12 +75,11 @@ hex = "0.4.3"
hickory-resolver = "0.25.2" hickory-resolver = "0.25.2"
hmac = "0.12.1" hmac = "0.12.1"
hyper = "1.6.0" hyper = "1.6.0"
tracing-ecs = "0.5.0"
hyper-rustls = { version = "0.27.7", default-features = false, features = [ hyper-rustls = { version = "0.27.7", default-features = false, features = [
"http1", "http1",
"native-tokio", "native-tokio",
"ring", "ring",
"tls12", "tls12",
] } ] }
hyper-util = "0.1.16" hyper-util = "0.1.16"
iana-time-zone = "0.1.63" iana-time-zone = "0.1.63"
@@ -86,15 +90,15 @@ itertools = "0.14.0"
jemalloc_pprof = "0.8.1" jemalloc_pprof = "0.8.1"
json-patch = { version = "4.0.0", default-features = false } json-patch = { version = "4.0.0", default-features = false }
lettre = { version = "0.11.18", default-features = false, features = [ lettre = { version = "0.11.18", default-features = false, features = [
"builder", "builder",
"hostname", "hostname",
"pool", "pool",
"ring", "ring",
"rustls", "rustls",
"rustls-native-certs", "rustls-native-certs",
"tokio1-rustls", "smtp-transport",
"smtp-transport", "tokio1",
"tokio1", "tokio1-rustls",
] } ] }
maxminddb = "0.26.0" maxminddb = "0.26.0"
meilisearch-sdk = { version = "0.29.1", default-features = false } meilisearch-sdk = { version = "0.29.1", default-features = false }
@@ -104,42 +108,45 @@ notify = { version = "8.2.0", default-features = false }
notify-debouncer-mini = { version = "0.7.0", default-features = false } notify-debouncer-mini = { version = "0.7.0", default-features = false }
p256 = "0.13.2" p256 = "0.13.2"
paste = "1.0.15" paste = "1.0.15"
path-util = { path = "packages/path-util" }
phf = { version = "0.12.1", features = ["macros"] } phf = { version = "0.12.1", features = ["macros"] }
png = "0.17.16" png = "0.17.16"
prometheus = "0.14.0" prometheus = "0.14.0"
quartz_nbt = "0.2.9" quartz_nbt = "0.2.9"
quick-xml = "0.38.1" quick-xml = "0.38.1"
rand = "=0.8.5" # Locked on 0.8 until argon2 and p256 update to 0.9 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 rand_chacha = "=0.3.1" # Locked on 0.3 until we can update rand to 0.9
redis = "0.32.4" redis = "0.32.4"
regex = "1.11.1" regex = "1.11.1"
reqwest = { version = "0.12.22", default-features = false } reqwest = { version = "0.12.22", default-features = false }
rgb = "0.8.52" rgb = "0.8.52"
rust_decimal = { version = "1.37.2", features = ["serde-with-float", "serde-with-str"] } rust_decimal = { version = "1.37.2", features = [
"serde-with-float",
"serde-with-str",
] }
rust_iso3166 = "0.1.14" rust_iso3166 = "0.1.14"
rust-s3 = { version = "0.35.1", default-features = false, features = [ rust-s3 = { version = "0.35.1", default-features = false, features = [
"fail-on-err", "fail-on-err",
"tags", "tags",
"tokio-rustls-tls", "tokio-rustls-tls",
] } ] }
rusty-money = "0.4.1" rusty-money = "0.4.1"
sentry = { version = "0.42.0", default-features = false, features = [ sentry = { version = "0.42.0", default-features = false, features = [
"backtrace", "backtrace",
"contexts", "contexts",
"debug-images", "debug-images",
"panic", "panic",
"reqwest", "reqwest",
"rustls", "rustls",
] } ] }
sentry-actix = "0.42.0" sentry-actix = "0.42.0"
const_format = "0.2.34"
serde = "1.0.219" serde = "1.0.219"
serde_bytes = "0.11.17" serde_bytes = "0.11.17"
serde_cbor = "0.11.2" serde_cbor = "0.11.2"
serde_ini = "0.2.0" serde_ini = "0.2.0"
serde_json = "1.0.142" serde_json = "1.0.142"
serde_with = "3.14.0" serde_with = "3.14.0"
serde-xml-rs = "0.8.1" # Also an XML (de)serializer, consider dropping yaserde in favor of this serde-xml-rs = "0.8.1" # Also an XML (de)serializer, consider dropping yaserde in favor of this
sha1 = "0.10.6" sha1 = "0.10.6"
sha1_smol = { version = "1.0.1", features = ["std"] } sha1_smol = { version = "1.0.1", features = ["std"] }
sha2 = "0.10.9" sha2 = "0.10.9"
@@ -156,8 +163,8 @@ tauri-plugin-opener = "2.4.0"
tauri-plugin-os = "2.3.0" tauri-plugin-os = "2.3.0"
tauri-plugin-single-instance = "2.3.2" tauri-plugin-single-instance = "2.3.2"
tauri-plugin-updater = { version = "2.9.0", default-features = false, features = [ tauri-plugin-updater = { version = "2.9.0", default-features = false, features = [
"rustls-tls", "rustls-tls",
"zip", "zip",
] } ] }
tauri-plugin-window-state = "2.4.0" tauri-plugin-window-state = "2.4.0"
tempfile = "3.20.0" tempfile = "3.20.0"
@@ -171,28 +178,28 @@ tokio-util = "0.7.16"
totp-rs = "5.7.0" totp-rs = "5.7.0"
tracing = "0.1.41" tracing = "0.1.41"
tracing-actix-web = { version = "0.7.19", default-features = false } tracing-actix-web = { version = "0.7.19", default-features = false }
tracing-ecs = "0.5.0"
tracing-error = "0.2.1" tracing-error = "0.2.1"
tracing-subscriber = "0.3.19" tracing-subscriber = "0.3.19"
eyre = "0.6.12" typed-path = "0.11.0"
color-eyre = "0.6.5"
url = "2.5.4" url = "2.5.4"
urlencoding = "2.1.3" urlencoding = "2.1.3"
uuid = "1.17.0" uuid = "1.17.0"
validator = "0.20.0" validator = "0.20.0"
webp = { version = "0.3.0", default-features = false } webp = { version = "0.3.0", default-features = false }
webview2-com = "0.38.0" # Should be updated in lockstep with wry
whoami = "1.6.0" whoami = "1.6.0"
windows = "0.61.3" windows = "0.61.3"
windows-core = "0.61.2" windows-core = "0.61.2"
webview2-com = "0.38.0" # Should be updated in lockstep with wry
winreg = "0.55.0" winreg = "0.55.0"
woothee = "0.13.0" woothee = "0.13.0"
yaserde = "0.12.0" yaserde = "0.12.0"
zbus = "5.9.0" zbus = "5.9.0"
zip = { version = "4.3.0", default-features = false, features = [ zip = { version = "4.3.0", default-features = false, features = [
"bzip2", "bzip2",
"deflate", "deflate",
"deflate64", "deflate64",
"zstd", "zstd",
] } ] }
zxcvbn = "3.1.0" zxcvbn = "3.1.0"
@@ -230,18 +237,18 @@ todo = "warn"
unnested_or_patterns = "warn" unnested_or_patterns = "warn"
wildcard_dependencies = "warn" wildcard_dependencies = "warn"
[profile.dev.package.sqlx-macros]
opt-level = 3
# Optimize for speed and reduce size on release builds # Optimize for speed and reduce size on release builds
[profile.release] [profile.release]
opt-level = "s" # Optimize for binary size opt-level = "s" # Optimize for binary size
strip = true # Remove debug symbols strip = true # Remove debug symbols
lto = true # Enables link to optimizations lto = true # Enables link to optimizations
panic = "abort" # Strip expensive panic clean-up logic panic = "abort" # Strip expensive panic clean-up logic
codegen-units = 1 # Compile crates one after another so the compiler can optimize better codegen-units = 1 # Compile crates one after another so the compiler can optimize better
# Specific profile for labrinth production builds # Specific profile for labrinth production builds
[profile.release-labrinth] [profile.release-labrinth]
inherits = "release" inherits = "release"
panic = "unwind" # Don't exit the whole app on panic in production panic = "unwind" # Don't exit the whole app on panic in production
[profile.dev.package.sqlx-macros]
opt-level = 3

19
_typos.toml Normal file
View File

@@ -0,0 +1,19 @@
[files]
extend-exclude = [
"**/src/locales/",
"apps/frontend/",
"patches/",
"packages/utils/",
"packages/ui/",
"packages/blog/",
# contains licenses like `CC-BY-ND-4.0`
"packages/moderation/src/data/stages/license.ts",
# contains payment card IDs like `IY1VMST1MOXS` which are flagged
"apps/labrinth/src/queue/payouts.rs",
]
[default.extend-words]
# Terms Of Use in `tou-link`
tou = "tou"
# Google Ad Manager
gam = "gam"

View File

@@ -1,14 +1,11 @@
[package] [package]
name = "theseus_playground" name = "theseus_playground"
version = "0.0.0"
edition.workspace = true edition.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
enumset = { workspace = true }
theseus = { workspace = true, features = ["cli"] } theseus = { workspace = true, features = ["cli"] }
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
enumset.workspace = true
[lints] [lints]
workspace = true workspace = true

View File

@@ -1,53 +1,51 @@
[package] [package]
name = "theseus_gui" name = "theseus_gui"
version = "1.0.0-local" # The actual version is set by the theseus-build workflow on tagging # The actual version is set by the theseus-build workflow on tagging
description = "The Modrinth App is a desktop application for managing your Minecraft mods" version = "1.0.0-local"
license = "GPL-3.0-only"
repository = "https://github.com/modrinth/code/apps/app/"
edition.workspace = true edition.workspace = true
description = "The Modrinth App is a desktop application for managing your Minecraft mods"
repository = "https://github.com/modrinth/code/apps/app/"
license = "GPL-3.0-only"
[dependencies]
chrono = { workspace = true }
daedalus = { workspace = true }
dashmap = { workspace = true }
either = { workspace = true }
enumset = { workspace = true, features = ["serde"] }
hyper = { workspace = true, features = ["server"] }
hyper-util = { workspace = true }
native-dialog = { workspace = true }
paste = { workspace = true }
path-util = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
serde_with = { workspace = true }
tauri = { workspace = true, features = [
"devtools",
"macos-private-api",
"protocol-asset",
] }
tauri-plugin-deep-link = { workspace = true }
tauri-plugin-dialog = { workspace = true }
tauri-plugin-http = { workspace = true }
tauri-plugin-opener = { workspace = true }
tauri-plugin-os = { workspace = true }
tauri-plugin-single-instance = { workspace = true }
tauri-plugin-updater = { workspace = true }
tauri-plugin-window-state = { workspace = true }
theseus = { workspace = true, features = ["tauri"] }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["time"] }
tracing = { workspace = true }
tracing-error = { workspace = true }
url = { workspace = true }
urlencoding = { workspace = true }
uuid = { workspace = true, features = ["serde", "v4"] }
[build-dependencies] [build-dependencies]
tauri-build = { workspace = true, features = ["codegen"] } tauri-build = { workspace = true, features = ["codegen"] }
[dependencies]
theseus = { workspace = true, features = ["tauri"] }
path-util.workspace = true
serde_json.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_with.workspace = true
tauri = { workspace = true, features = ["devtools", "macos-private-api", "protocol-asset"] }
tauri-plugin-deep-link.workspace = true
tauri-plugin-dialog.workspace = true
tauri-plugin-http.workspace = true
tauri-plugin-opener.workspace = true
tauri-plugin-os.workspace = true
tauri-plugin-single-instance.workspace = true
tauri-plugin-updater.workspace = true
tauri-plugin-window-state.workspace = true
tokio = { workspace = true, features = ["time"] }
thiserror.workspace = true
daedalus.workspace = true
chrono.workspace = true
either.workspace = true
hyper = { workspace = true, features = ["server"] }
hyper-util.workspace = true
url.workspace = true
urlencoding.workspace = true
uuid = { workspace = true, features = ["serde", "v4"] }
tracing.workspace = true
tracing-error.workspace = true
dashmap.workspace = true
paste.workspace = true
enumset = { workspace = true, features = ["serde"] }
native-dialog.workspace = true
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
tauri-plugin-updater = { workspace = true, optional = true } tauri-plugin-updater = { workspace = true, optional = true }

File diff suppressed because one or more lines are too long

View File

@@ -272,7 +272,7 @@ pub struct EditProfile {
#[serde( #[serde(
default, default,
skip_serializing_if = "Option::is_none", skip_serializing_if = "Option::is_none",
with = "::serde_with::rust::double_option" with = "serde_with::rust::double_option"
)] )]
pub loader_version: Option<Option<String>>, pub loader_version: Option<Option<String>>,
@@ -281,45 +281,45 @@ pub struct EditProfile {
#[serde( #[serde(
default, default,
skip_serializing_if = "Option::is_none", skip_serializing_if = "Option::is_none",
with = "::serde_with::rust::double_option" with = "serde_with::rust::double_option"
)] )]
pub linked_data: Option<Option<LinkedData>>, pub linked_data: Option<Option<LinkedData>>,
#[serde( #[serde(
default, default,
skip_serializing_if = "Option::is_none", skip_serializing_if = "Option::is_none",
with = "::serde_with::rust::double_option" with = "serde_with::rust::double_option"
)] )]
pub java_path: Option<Option<String>>, pub java_path: Option<Option<String>>,
#[serde( #[serde(
default, default,
skip_serializing_if = "Option::is_none", skip_serializing_if = "Option::is_none",
with = "::serde_with::rust::double_option" with = "serde_with::rust::double_option"
)] )]
pub extra_launch_args: Option<Option<Vec<String>>>, pub extra_launch_args: Option<Option<Vec<String>>>,
#[serde( #[serde(
default, default,
skip_serializing_if = "Option::is_none", skip_serializing_if = "Option::is_none",
with = "::serde_with::rust::double_option" with = "serde_with::rust::double_option"
)] )]
pub custom_env_vars: Option<Option<Vec<(String, String)>>>, pub custom_env_vars: Option<Option<Vec<(String, String)>>>,
#[serde( #[serde(
default, default,
skip_serializing_if = "Option::is_none", skip_serializing_if = "Option::is_none",
with = "::serde_with::rust::double_option" with = "serde_with::rust::double_option"
)] )]
pub memory: Option<Option<MemorySettings>>, pub memory: Option<Option<MemorySettings>>,
#[serde( #[serde(
default, default,
skip_serializing_if = "Option::is_none", skip_serializing_if = "Option::is_none",
with = "::serde_with::rust::double_option" with = "serde_with::rust::double_option"
)] )]
pub force_fullscreen: Option<Option<bool>>, pub force_fullscreen: Option<Option<bool>>,
#[serde( #[serde(
default, default,
skip_serializing_if = "Option::is_none", skip_serializing_if = "Option::is_none",
with = "::serde_with::rust::double_option" with = "serde_with::rust::double_option"
)] )]
pub game_resolution: Option<Option<WindowSize>>, pub game_resolution: Option<Option<WindowSize>>,
pub hooks: Option<Hooks>, pub hooks: Option<Hooks>,

View File

@@ -1,39 +1,39 @@
[package] [package]
name = "daedalus_client" name = "daedalus_client"
version = "0.2.2" version = "0.2.2"
authors = ["Jai A <jai@modrinth.com>"]
edition.workspace = true edition.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
daedalus.workspace = true
tokio = { workspace = true, features = ["sync", "macros", "rt-multi-thread"] }
futures.workspace = true
dotenvy.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
serde-xml-rs.workspace = true
thiserror.workspace = true
reqwest = { workspace = true, features = ["stream", "json", "rustls-tls-native-roots"] }
async_zip = { workspace = true, features = [ async_zip = { workspace = true, features = [
"chrono", "bzip2",
"tokio-fs", "chrono",
"deflate", "deflate",
"bzip2", "deflate64",
"zstd", "tokio-fs",
"deflate64", "zstd",
] } ] }
bytes = { workspace = true }
chrono = { workspace = true, features = ["serde"] } chrono = { workspace = true, features = ["serde"] }
bytes.workspace = true daedalus = { workspace = true }
rust-s3.workspace = true dashmap = { workspace = true }
dashmap.workspace = true dotenvy = { workspace = true }
sha1_smol.workspace = true futures = { workspace = true }
indexmap = { workspace = true, features = ["serde"] } indexmap = { workspace = true, features = ["serde"] }
itertools.workspace = true itertools = { workspace = true }
tracing-error.workspace = true reqwest = { workspace = true, features = [
"json",
tracing.workspace = true "rustls-tls-native-roots",
"stream",
] }
rust-s3 = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
serde-xml-rs = { workspace = true }
sha1_smol = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync"] }
tracing = { workspace = true }
tracing-error = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] } tracing-subscriber = { workspace = true, features = ["env-filter"] }
[lints] [lints]

View File

@@ -38,9 +38,9 @@ Once the user is ready to authorize your application, you need to construct a UR
| `state` | A mechanism to prevent certain attacks. Explained further below. Recommended but optional | | `state` | A mechanism to prevent certain attacks. Explained further below. Recommended but optional |
| `redirect_uri` | The URI the user is redirect to after finishing authorization | | `redirect_uri` | The URI the user is redirect to after finishing authorization |
You might have noticed the `state` parameter. [CSRF] (Cross-site request forgery), and [clickjacking] are security vulnerabilities that you're recommended to protect against. In OAuth2 this is usually done with the `state` parameter. When the user initiates a request to start authorization, you include a `state` which is unique to this request. This can, for example, be saved in localStorge or a cookie. When the redirect URI is called, you verify that the `state` parameter is the same. Using `state` is optional, but recommended. You might have noticed the `state` parameter. [CSRF] (Cross-site request forgery), and [clickjacking] are security vulnerabilities that you're recommended to protect against. In OAuth2 this is usually done with the `state` parameter. When the user initiates a request to start authorization, you include a `state` which is unique to this request. This can, for example, be saved in localStorage or a cookie. When the redirect URI is called, you verify that the `state` parameter is the same. Using `state` is optional, but recommended.
The scope identifiers are currently best found in the backend source code located at [`apps/labrinth/src/models/v3/pats.rs`]. The scope parameter is an array of scope identifiers, seperated by a plus sign (`+`). The scope identifiers are currently best found in the backend source code located at [`apps/labrinth/src/models/v3/pats.rs`]. The scope parameter is an array of scope identifiers, separated by a plus sign (`+`).
The redirect URI is the endpoint on your server that will receive the code which can eventually be used to act on the user's behalf. For security reasons the redirect URI used has to be allowlisted in your application settings. The redirect will contain the following query parameters: The redirect URI is the endpoint on your server that will receive the code which can eventually be used to act on the user's behalf. For security reasons the redirect URI used has to be allowlisted in your application settings. The redirect will contain the following query parameters:

View File

@@ -1,7 +1,6 @@
[package] [package]
name = "labrinth" name = "labrinth"
version = "2.7.0" version = "2.7.0"
authors = ["geometrically <jai@modrinth.com>"]
edition.workspace = true edition.workspace = true
license = "AGPL-3.0-only" license = "AGPL-3.0-only"
@@ -11,148 +10,140 @@ name = "labrinth"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
actix-web.workspace = true actix-cors = { workspace = true }
actix-rt.workspace = true actix-files = { workspace = true }
actix-multipart.workspace = true actix-multipart = { workspace = true }
actix-cors.workspace = true actix-rt = { workspace = true }
actix-ws.workspace = true actix-web = { workspace = true }
actix-files.workspace = true
prometheus.workspace = true
actix-web-prom = { workspace = true, features = ["process"] } actix-web-prom = { workspace = true, features = ["process"] }
actix-ws = { workspace = true }
tracing.workspace = true argon2 = { workspace = true }
tracing-actix-web.workspace = true ariadne = { workspace = true }
console-subscriber.workspace = true async-stripe = { workspace = true, features = [
tracing-subscriber.workspace = true "billing",
tracing-ecs.workspace = true "checkout",
eyre.workspace = true "connect",
color-eyre.workspace = true "webhook-events",
] }
tokio = { workspace = true, features = ["sync", "rt-multi-thread"] } async-trait = { workspace = true }
tokio-stream.workspace = true base64 = { workspace = true }
bitflags = { workspace = true }
futures.workspace = true bytes = { workspace = true }
futures-util.workspace = true censor = { workspace = true }
async-trait.workspace = true
dashmap.workspace = true
paste.workspace = true
meilisearch-sdk = { workspace = true, features = ["reqwest"] }
rust-s3.workspace = true
reqwest = { workspace = true, features = ["http2", "rustls-tls-webpki-roots", "json", "multipart"] }
hyper-rustls.workspace = true
hyper-util.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
serde_with.workspace = true
chrono = { workspace = true, features = ["serde"] } chrono = { workspace = true, features = ["serde"] }
yaserde = { workspace = true, features = ["derive"] }
rand.workspace = true
rand_chacha.workspace = true
bytes.workspace = true
base64.workspace = true
sha1.workspace = true
sha2.workspace = true
hmac.workspace = true
argon2.workspace = true
murmur2.workspace = true
bitflags.workspace = true
hex.workspace = true
zxcvbn.workspace = true
totp-rs = { workspace = true, features = ["gen_secret"] }
const_format.workspace = true
url.workspace = true
urlencoding.workspace = true
zip.workspace = true
itertools.workspace = true
validator = { workspace = true, features = ["derive"] }
regex.workspace = true
censor.workspace = true
spdx = { workspace = true, features = ["text"] }
dotenvy.workspace = true
thiserror.workspace = true
either.workspace = true
sqlx = { workspace = true, features = [
"runtime-tokio",
"tls-rustls-ring",
"postgres",
"chrono",
"macros",
"migrate",
"rust_decimal",
"json",
] }
rust_decimal = { workspace = true, features = ["serde-with-float", "serde-with-str"] }
redis = { workspace = true, features = ["tokio-comp", "ahash", "r2d2"] }
deadpool-redis.workspace = true
clickhouse = { workspace = true, features = ["uuid", "time"] }
uuid = { workspace = true, features = ["v4", "fast-rng", "serde"] }
maxminddb.workspace = true
flate2.workspace = true
tar.workspace = true
sentry.workspace = true
sentry-actix.workspace = true
image = { workspace = true, features = [
"avif",
"bmp",
"dds",
"exr",
"ff",
"gif",
"hdr",
"ico",
"jpeg",
"png",
"pnm",
"qoi",
"tga",
"tiff",
"webp",
] }
color-thief.workspace = true
webp.workspace = true
woothee.workspace = true
lettre.workspace = true
rust_iso3166.workspace = true
async-stripe = { workspace = true, features = ["billing", "checkout", "connect", "webhook-events"] }
rusty-money.workspace = true
json-patch.workspace = true
ariadne.workspace = true
path-util.workspace = true
clap = { workspace = true, features = ["derive"] } clap = { workspace = true, features = ["derive"] }
clickhouse = { workspace = true, features = ["time", "uuid"] }
[target.'cfg(target_os = "linux")'.dependencies] color-eyre = { workspace = true }
tikv-jemallocator = { workspace = true, features = [ color-thief = { workspace = true }
"profiling", console-subscriber = { workspace = true }
"unprefixed_malloc_on_supported_platforms", const_format = { workspace = true }
dashmap = { workspace = true }
deadpool-redis.workspace = true
dotenvy = { workspace = true }
either = { workspace = true }
eyre = { workspace = true }
flate2 = { workspace = true }
futures = { workspace = true }
futures-util = { workspace = true }
hex = { workspace = true }
hmac = { workspace = true }
hyper-rustls = { workspace = true }
hyper-util = { workspace = true }
image = { workspace = true, features = [
"avif",
"bmp",
"dds",
"exr",
"ff",
"gif",
"hdr",
"ico",
"jpeg",
"png",
"pnm",
"qoi",
"tga",
"tiff",
"webp",
] } ] }
tikv-jemalloc-ctl = { workspace = true, features = ["stats"] } itertools = { workspace = true }
jemalloc_pprof = { workspace = true, features = ["flamegraph"] } json-patch = { workspace = true }
lettre = { workspace = true }
maxminddb = { workspace = true }
meilisearch-sdk = { workspace = true, features = ["reqwest"] }
murmur2 = { workspace = true }
paste = { workspace = true }
path-util = { workspace = true }
prometheus = { workspace = true }
rand = { workspace = true }
rand_chacha = { workspace = true }
redis = { workspace = true, features = ["ahash", "r2d2", "tokio-comp"] }
regex = { workspace = true }
reqwest = { workspace = true, features = [
"http2",
"json",
"multipart",
"rustls-tls-webpki-roots",
] }
rust_decimal = { workspace = true, features = [
"serde-with-float",
"serde-with-str",
] }
rust_iso3166 = { workspace = true }
rust-s3 = { workspace = true }
rusty-money = { workspace = true }
sentry = { workspace = true }
sentry-actix = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
serde_with = { workspace = true }
sha1 = { workspace = true }
sha2 = { workspace = true }
spdx = { workspace = true, features = ["text"] }
sqlx = { workspace = true, features = [
"chrono",
"json",
"macros",
"migrate",
"postgres",
"runtime-tokio",
"rust_decimal",
"tls-rustls-ring",
] }
tar = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "sync"] }
tokio-stream = { workspace = true }
totp-rs = { workspace = true, features = ["gen_secret"] }
tracing = { workspace = true }
tracing-actix-web = { workspace = true }
tracing-ecs = { workspace = true }
tracing-subscriber = { workspace = true }
url = { workspace = true }
urlencoding = { workspace = true }
uuid = { workspace = true, features = ["fast-rng", "serde", "v4"] }
validator = { workspace = true, features = ["derive"] }
webp = { workspace = true }
woothee = { workspace = true }
yaserde = { workspace = true, features = ["derive"] }
zip = { workspace = true }
zxcvbn = { workspace = true }
[dev-dependencies] [dev-dependencies]
actix-http.workspace = true actix-http = { workspace = true }
[build-dependencies] [build-dependencies]
dotenv-build.workspace = true chrono = { workspace = true }
chrono.workspace = true dotenv-build = { workspace = true }
iana-time-zone.workspace = true iana-time-zone = { workspace = true }
[target.'cfg(target_os = "linux")'.dependencies]
jemalloc_pprof = { workspace = true, features = ["flamegraph"] }
tikv-jemalloc-ctl = { workspace = true, features = ["stats"] }
tikv-jemallocator = { workspace = true, features = [
"profiling",
"unprefixed_malloc_on_supported_platforms",
] }
[lints] [lints]
workspace = true workspace = true

View File

@@ -20,7 +20,7 @@ use validator::Validate;
/// A project returned from the API /// A project returned from the API
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
pub struct LegacyProject { pub struct LegacyProject {
/// Relevant V2 fields- these were removed or modfified in V3, /// Relevant V2 fields- these were removed or modified in V3,
/// and are now part of the dynamic fields system /// and are now part of the dynamic fields system
/// The support range for the client project* /// The support range for the client project*
pub client_side: LegacySideType, pub client_side: LegacySideType,
@@ -269,7 +269,7 @@ impl std::fmt::Display for LegacySideType {
} }
impl LegacySideType { impl LegacySideType {
// These are constant, so this can remove unneccessary allocations (`to_string`) // These are constant, so this can remove unnecessary allocations (`to_string`)
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
match self { match self {
LegacySideType::Required => "required", LegacySideType::Required => "required",
@@ -292,7 +292,7 @@ impl LegacySideType {
/// A specific version of a project /// A specific version of a project
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
pub struct LegacyVersion { pub struct LegacyVersion {
/// Relevant V2 fields- these were removed or modfified in V3, /// Relevant V2 fields- these were removed or modified in V3,
/// and are now part of the dynamic fields system /// and are now part of the dynamic fields system
/// A list of game versions this project supports /// A list of game versions this project supports
pub game_versions: Vec<String>, pub game_versions: Vec<String>,

View File

@@ -22,7 +22,7 @@ pub struct Collection {
/// Color of the collection. /// Color of the collection.
pub color: Option<u32>, pub color: Option<u32>,
/// The status of the collectin (eg: whether collection is public or not) /// The status of the collection (eg: whether collection is public or not)
pub status: CollectionStatus, pub status: CollectionStatus,
/// The date at which the collection was first published. /// The date at which the collection was first published.

View File

@@ -650,7 +650,7 @@ impl NotificationDeliveryStatus {
NotificationDeliveryStatus::Delivered => Ok(()), NotificationDeliveryStatus::Delivered => Ok(()),
NotificationDeliveryStatus::SkippedPreferences | NotificationDeliveryStatus::SkippedPreferences |
NotificationDeliveryStatus::SkippedDefault | NotificationDeliveryStatus::SkippedDefault |
NotificationDeliveryStatus::Pending => Err(ApiError::InvalidInput("An error occured while sending an email to your email address. Please try again later.".to_owned())), NotificationDeliveryStatus::Pending => Err(ApiError::InvalidInput("An error occurred while sending an email to your email address. Please try again later.".to_owned())),
NotificationDeliveryStatus::PermanentlyFailed => Err(ApiError::InvalidInput("This email address doesn't exist! Please try another one.".to_owned())), NotificationDeliveryStatus::PermanentlyFailed => Err(ApiError::InvalidInput("This email address doesn't exist! Please try another one.".to_owned())),
} }
} }

View File

@@ -20,7 +20,7 @@ pub struct Project {
pub id: ProjectId, pub id: ProjectId,
/// The slug of a project, used for vanity URLs /// The slug of a project, used for vanity URLs
pub slug: Option<String>, pub slug: Option<String>,
/// The aggregated project typs of the versions of this project /// The aggregated project typos of the versions of this project
pub project_types: Vec<String>, pub project_types: Vec<String>,
/// The aggregated games of the versions of this project /// The aggregated games of the versions of this project
pub games: Vec<String>, pub games: Vec<String>,
@@ -49,7 +49,7 @@ pub struct Project {
/// The status of the project /// The status of the project
pub status: ProjectStatus, pub status: ProjectStatus,
/// The requested status of this projct /// The requested status of this project
pub requested_status: Option<ProjectStatus>, pub requested_status: Option<ProjectStatus>,
/// DEPRECATED: moved to threads system /// DEPRECATED: moved to threads system

View File

@@ -65,7 +65,7 @@ impl std::fmt::Display for ThreadType {
} }
impl ThreadType { impl ThreadType {
// These are constant, so this can remove unneccessary allocations (`to_string`) // These are constant, so this can remove unnecessary allocations (`to_string`)
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
match self { match self {
ThreadType::Report => "report", ThreadType::Report => "report",

View File

@@ -502,7 +502,7 @@ async fn update_anrok_transactions(
/// Attempts to process a user redeemal. /// Attempts to process a user redeemal.
/// ///
/// Returns `Ok` if the entry has been succesfully processed, or will not be processed. /// Returns `Ok` if the entry has been successfully processed, or will not be processed.
pub async fn try_process_user_redeemal( pub async fn try_process_user_redeemal(
pool: &PgPool, pool: &PgPool,
redis: &RedisPool, redis: &RedisPool,

View File

@@ -75,7 +75,7 @@ pub enum AttachedCharge {
/// This can be used in the case of resubscription flows. The amount from this /// This can be used in the case of resubscription flows. The amount from this
/// charge will be used, but the tax will be recalculated and the charge updated. /// charge will be used, but the tax will be recalculated and the charge updated.
/// ///
/// The charge's status will NOT be updated - it is the caller's responsability to /// The charge's status will NOT be updated - it is the caller's responsibility to
/// update the charge's status on failure or success. /// update the charge's status on failure or success.
/// ///
/// This may be accompanied by an automated payment session. /// This may be accompanied by an automated payment session.
@@ -160,7 +160,7 @@ pub struct PaymentBootstrapOptions<'a> {
/// ///
/// Taxes will always be collected. /// Taxes will always be collected.
/// ///
/// Note the charge will NOT be updated. It is the caller's responsability to update the charge /// Note the charge will NOT be updated. It is the caller's responsibility to update the charge
/// on success or failure. /// on success or failure.
pub payment_session: PaymentSession, pub payment_session: PaymentSession,
/// The charge the payment intent on should be based upon. /// The charge the payment intent on should be based upon.
@@ -185,7 +185,7 @@ pub struct PaymentBootstrapResults {
/// ///
/// # Important notes /// # Important notes
/// ///
/// - This function does not perform any database writes. It is the caller's responsability to, for /// - This function does not perform any database writes. It is the caller's responsibility to, for
/// example, update the charge's status on success or failure, or update the charge's tax amount, /// example, update the charge's status on success or failure, or update the charge's tax amount,
/// tax eligibility or payment and tax platform IDs. /// tax eligibility or payment and tax platform IDs.
/// - You may not update or create a payment intent for an off-session payment flow without /// - You may not update or create a payment intent for an off-session payment flow without

View File

@@ -1,4 +1,4 @@
//! TODO: this module should be removed; it is superceded by `analytics_get` //! TODO: this module should be removed; it is superseded by `analytics_get`
use super::ApiError; use super::ApiError;
use crate::database; use crate::database;
@@ -412,7 +412,7 @@ pub async fn revenue_get(
/// Get country data for a set of projects or versions /// Get country data for a set of projects or versions
/// Data is returned as a hashmap of project/version ids to a hashmap of coutnry to downloads. /// Data is returned as a hashmap of project/version ids to a hashmap of coutnry to downloads.
/// Unknown countries are labeled "". /// Unknown countries are labeled "".
/// This is usuable to see significant performing countries per project /// This is usable to see significant performing countries per project
/// eg: /// eg:
/// { /// {
/// "4N1tEhnO": { /// "4N1tEhnO": {
@@ -485,7 +485,7 @@ pub async fn countries_downloads_get(
/// Get country data for a set of projects or versions /// Get country data for a set of projects or versions
/// Data is returned as a hashmap of project/version ids to a hashmap of coutnry to views. /// Data is returned as a hashmap of project/version ids to a hashmap of coutnry to views.
/// Unknown countries are labeled "". /// Unknown countries are labeled "".
/// This is usuable to see significant performing countries per project /// This is usable to see significant performing countries per project
/// eg: /// eg:
/// { /// {
/// "4N1tEhnO": { /// "4N1tEhnO": {

View File

@@ -355,7 +355,7 @@ pub async fn index_local(
} }
// SPECIAL BEHAVIOUR: // SPECIAL BEHAVIOUR:
// For consitency with v2 searching, we manually input the // For consistency with v2 searching, we manually input the
// client_side and server_side fields from the loader fields into // client_side and server_side fields from the loader fields into
// separate loader fields. // separate loader fields.
// 'client_side' and 'server_side' remain supported by meilisearch even though they are no longer v3 fields. // 'client_side' and 'server_side' remain supported by meilisearch even though they are no longer v3 fields.

View File

@@ -33,7 +33,7 @@ pub struct FormResponse {
pub company_name: String, pub company_name: String,
pub company_email: String, pub company_email: String,
pub reference_id: String, pub reference_id: String,
/// This is a DateTime, but it's not consistent wether it has a /// This is a DateTime, but it's not consistent whether it has a
/// timezone or not, so we just parse it as a string and use [`Utc::now()`](fn@chrono::Utc::now) /// timezone or not, so we just parse it as a string and use [`Utc::now()`](fn@chrono::Utc::now)
/// rather than using the provided DateTime. /// rather than using the provided DateTime.
pub signed_at: Option<String>, pub signed_at: Option<String>,

View File

@@ -198,7 +198,7 @@ pub async fn delete_old_images(
// check changes to associated images // check changes to associated images
// if they no longer exist in the String list, delete them // if they no longer exist in the String list, delete them
// Eg: if description is modified and no longer contains a link to an iamge // Eg: if description is modified and no longer contains a link to an image
pub async fn delete_unused_images( pub async fn delete_unused_images(
context: ImageContext, context: ImageContext,
reference_strings: Vec<&str>, reference_strings: Vec<&str>,

View File

@@ -129,7 +129,7 @@ impl ApiVersion for ApiV3 {
modify_json, modify_json,
); );
// Add a versiom. // Add a version.
let req = TestRequest::post() let req = TestRequest::post()
.uri("/v3/version") .uri("/v3/version")
.append_pat(pat) .append_pat(pat)

View File

@@ -59,7 +59,7 @@ impl TemporaryDatabase {
let database_url = let database_url =
dotenvy::var("DATABASE_URL").expect("No database URL"); dotenvy::var("DATABASE_URL").expect("No database URL");
// Create the temporary (and template datbase, if needed) // Create the temporary (and template database, if needed)
Self::create_temporary(&database_url, &temp_database_name).await; Self::create_temporary(&database_url, &temp_database_name).await;
// Pool to the temporary database // Pool to the temporary database
@@ -102,7 +102,7 @@ impl TemporaryDatabase {
} }
} }
// Creates a template and temporary databse (panics) // Creates a template and temporary database (panics)
// 1. Waits to obtain a pg lock on the main database // 1. Waits to obtain a pg lock on the main database
// 2. Creates a new template database called 'TEMPLATE_DATABASE_NAME', if needed // 2. Creates a new template database called 'TEMPLATE_DATABASE_NAME', if needed
// 3. Switches to the template database // 3. Switches to the template database

View File

@@ -390,7 +390,7 @@ pub async fn add_project_beta(api: &ApiV3) -> (Project, Version) {
} }
pub async fn add_organization_zeta(api: &ApiV3) -> Organization { pub async fn add_organization_zeta(api: &ApiV3) -> Organization {
// Add an organzation. // Add an organization.
let req = TestRequest::post() let req = TestRequest::post()
.uri("/v3/organization") .uri("/v3/organization")
.append_pat(USER_USER_PAT) .append_pat(USER_USER_PAT)

View File

@@ -35,7 +35,7 @@ pub struct PermissionsTest<'a, A: Api> {
user_pat: Option<&'a str>, user_pat: Option<&'a str>,
// Whether or not the user ID should be removed from the project/organization team after the test // Whether or not the user ID should be removed from the project/organization team after the test
// (This is mostly reelvant if you are also using an existing project/organization, and want to do // (This is mostly relevant if you are also using an existing project/organization, and want to do
// multiple tests with the same user. // multiple tests with the same user.
remove_user: bool, remove_user: bool,
@@ -1056,10 +1056,10 @@ async fn create_dummy_org(setup_api: &ApiV3) -> (String, String) {
let organization = setup_api let organization = setup_api
.get_organization_deserialized(&slug, ADMIN_USER_PAT) .get_organization_deserialized(&slug, ADMIN_USER_PAT)
.await; .await;
let organizaion_id = organization.id.to_string(); let organization_id = organization.id.to_string();
let team_id = organization.team_id.to_string(); let team_id = organization.team_id.to_string();
(organizaion_id, team_id) (organization_id, team_id)
} }
async fn add_project_to_org( async fn add_project_to_org(

View File

@@ -61,7 +61,7 @@ pub async fn pat_full_test() {
let mock_pat_test = |token: &str| { let mock_pat_test = |token: &str| {
let token = token.to_string(); let token = token.to_string();
async { async {
// This uses a route directly instead of an api call because it doesn't relaly matter and we // This uses a route directly instead of an api call because it doesn't really matter and we
// want it to succeed no matter what. // want it to succeed no matter what.
// This is an arbitrary request. // This is an arbitrary request.
let req = test::TestRequest::post() let req = test::TestRequest::post()

View File

@@ -419,7 +419,7 @@ pub async fn test_patch_project() {
.await; .await;
assert_status!(&resp, StatusCode::UNAUTHORIZED); assert_status!(&resp, StatusCode::UNAUTHORIZED);
// Sucessful request to patch many fields. // Successful request to patch many fields.
let resp = api let resp = api
.edit_project( .edit_project(
alpha_project_slug, alpha_project_slug,
@@ -500,7 +500,7 @@ pub async fn test_patch_v3() {
let alpha_project_slug = &test_env.dummy.project_alpha.project_slug; let alpha_project_slug = &test_env.dummy.project_alpha.project_slug;
// Sucessful request to patch many fields. // Successful request to patch many fields.
let resp = api let resp = api
.edit_project( .edit_project(
alpha_project_slug, alpha_project_slug,
@@ -1141,7 +1141,7 @@ async fn permissions_delete_project() {
async fn project_permissions_consistency_test() { async fn project_permissions_consistency_test() {
with_test_environment_all(Some(10), |test_env| async move { with_test_environment_all(Some(10), |test_env| async move {
// Test that the permissions are consistent with each other // Test that the permissions are consistent with each other
// For example, if we get the projectpermissions directly, from an organization's defaults, overriden, etc, they should all be correct & consistent // For example, if we get the projectpermissions directly, from an organization's defaults, overridden, etc, they should all be correct & consistent
let api = &test_env.api; let api = &test_env.api;
// Full project permissions test with EDIT_DETAILS // Full project permissions test with EDIT_DETAILS
let success_permissions = ProjectPermissions::EDIT_DETAILS; let success_permissions = ProjectPermissions::EDIT_DETAILS;

View File

@@ -535,7 +535,7 @@ async fn test_patch_organization_team_member() {
}).await; }).await;
} }
// trasnfer ownership (requires being owner, etc) // transfer ownership (requires being owner, etc)
#[actix_rt::test] #[actix_rt::test]
async fn transfer_ownership_v3() { async fn transfer_ownership_v3() {
// Test setup and dummy data // Test setup and dummy data
@@ -675,7 +675,7 @@ async fn transfer_ownership_v3() {
// // Adding a user to a project team in an organization, when that user is in the organization but not the team, // // Adding a user to a project team in an organization, when that user is in the organization but not the team,
// // should have those permissions apply regardless of whether the user has accepted the invite or not. // // should have those permissions apply regardless of whether the user has accepted the invite or not.
// // This is because project-team permission overrriding must be possible, and this overriding can decrease the number of permissions a user has. // // This is because project-team permission overriding must be possible, and this overriding can decrease the number of permissions a user has.
// let test_env = TestEnvironment::build(None).await; // let test_env = TestEnvironment::build(None).await;
// let api = &test_env.api; // let api = &test_env.api;

View File

@@ -103,7 +103,7 @@ async fn test_project_type_sanity() {
); );
} }
// As we get more complicated strucures with as v3 continues to expand, and alpha/beta get more complicated, we should add more tests here, // As we get more complicated structures with as v3 continues to expand, and alpha/beta get more complicated, we should add more tests here,
// to ensure that projects created with v3 routes are still valid and work with v3 routes. // to ensure that projects created with v3 routes are still valid and work with v3 routes.
}, },
) )
@@ -413,7 +413,7 @@ pub async fn test_patch_v2() {
let alpha_project_slug = &test_env.dummy.project_alpha.project_slug; let alpha_project_slug = &test_env.dummy.project_alpha.project_slug;
// Sucessful request to patch many fields. // Successful request to patch many fields.
let resp = api let resp = api
.edit_project( .edit_project(
alpha_project_slug, alpha_project_slug,
@@ -433,7 +433,7 @@ pub async fn test_patch_v2() {
// Note: the original V2 value of this was "optional", // Note: the original V2 value of this was "optional",
// but Required/Optional is no longer a carried combination in v3, as the changes made were lossy. // but Required/Optional is no longer a carried combination in v3, as the changes made were lossy.
// Now, the test Required/Unsupported combination is tested instead. // Now, the test Required/Unsupported combination is tested instead.
// Setting Required/Optional in v2 will not work, this is known and accepteed. // Setting Required/Optional in v2 will not work, this is known and accepted.
assert_eq!(project.client_side.as_str(), "unsupported"); assert_eq!(project.client_side.as_str(), "unsupported");
assert_eq!(project.server_side.as_str(), "required"); assert_eq!(project.server_side.as_str(), "required");
}, },

View File

@@ -15,7 +15,7 @@ use crate::{
}, },
}; };
// trasnfer ownership (requires being owner, etc) // transfer ownership (requires being owner, etc)
#[actix_rt::test] #[actix_rt::test]
async fn transfer_ownership_v2() { async fn transfer_ownership_v2() {
// Test setup and dummy data // Test setup and dummy data

View File

@@ -56,7 +56,7 @@ pub async fn test_patch_version() {
assert_status!(&resp, StatusCode::BAD_REQUEST); assert_status!(&resp, StatusCode::BAD_REQUEST);
} }
// Sucessful request to patch many fields. // Successful request to patch many fields.
let resp = api let resp = api
.edit_version( .edit_version(
alpha_version_id, alpha_version_id,

View File

@@ -3,7 +3,7 @@ pub mod common;
// Not all tests expect exactly the same functionality in v2 and v3. // Not all tests expect exactly the same functionality in v2 and v3.
// For example, though we expect the /GET version to return the corresponding project, // For example, though we expect the /GET version to return the corresponding project,
// we may want to do different checks for each. // we may want to do different checks for each.
// (such as checking client_side in v2, but loader fields on v3- which are model-exclusie) // (such as checking client_side in v2, but loader fields on v3- which are model-exclusive)
// Such V2 tests are exported here // Such V2 tests are exported here
mod v2 { mod v2 {

View File

@@ -439,7 +439,7 @@ pub async fn test_patch_version() {
assert_status!(&resp, StatusCode::BAD_REQUEST); assert_status!(&resp, StatusCode::BAD_REQUEST);
} }
// Sucessful request to patch many fields. // Successful request to patch many fields.
let resp = api let resp = api
.edit_version( .edit_version(
alpha_version_id, alpha_version_id,

View File

@@ -1,136 +1,129 @@
[package] [package]
name = "theseus" name = "theseus"
version = "1.0.0-local" # The actual version is set by the theseus-build workflow on tagging # The actual version is set by the theseus-build workflow on tagging
authors = ["Jai A <jaiagr+gpg@pm.me>"] version = "1.0.0-local"
edition.workspace = true edition.workspace = true
[dependencies] [dependencies]
ariadne = { workspace = true }
async-compression = { workspace = true, features = ["gzip", "tokio"] }
async-recursion = { workspace = true }
async-tungstenite = { workspace = true, features = [
"tokio-runtime",
"tokio-rustls-webpki-roots",
] }
async-walkdir = { workspace = true }
async_zip = { workspace = true, features = [
"bzip2",
"chrono",
"deflate",
"deflate64",
"tokio-fs",
"zstd",
] }
base64 = { workspace = true }
bytemuck = { workspace = true, features = ["extern_crate_alloc"] }
bytes = { workspace = true, features = ["serde"] } bytes = { workspace = true, features = ["serde"] }
chardetng = { workspace = true }
chrono = { workspace = true, features = ["serde"] }
daedalus = { workspace = true }
dashmap = { workspace = true, features = ["serde"] }
data-url = { workspace = true }
derive_more = { workspace = true, features = ["display"] }
dirs = { workspace = true }
discord-rich-presence = { workspace = true }
dunce = { workspace = true }
either = { workspace = true }
encoding_rs = { workspace = true }
enumset = { workspace = true }
flate2 = { workspace = true }
fs4 = { workspace = true, features = ["tokio"] }
futures = { workspace = true, features = ["alloc", "async-await"] }
heck = { workspace = true }
hickory-resolver = { workspace = true }
indicatif = { workspace = true, optional = true }
itertools = { workspace = true }
notify = { workspace = true }
notify-debouncer-mini = { workspace = true }
p256 = { workspace = true, features = ["ecdsa"] }
paste = { workspace = true }
path-util = { workspace = true }
phf = { workspace = true }
png = { workspace = true }
quartz_nbt = { workspace = true, features = ["serde"] }
quick-xml = { workspace = true, features = ["async-tokio"] }
rand = { workspace = true }
regex = { workspace = true }
reqwest = { workspace = true, features = [
"brotli",
"charset",
"deflate",
"gzip",
"http2",
"json",
"macos-system-configuration",
"multipart",
"rustls-tls-webpki-roots",
"stream",
] }
rgb = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true serde_ini = { workspace = true }
serde_ini.workspace = true serde_json = { workspace = true }
serde_with.workspace = true serde_with = { workspace = true }
sha1_smol.workspace = true sha1_smol = { workspace = true }
sha2.workspace = true sha2 = { workspace = true }
sqlx = { workspace = true, features = [
"json",
"macros",
"migrate",
"runtime-tokio",
"sqlite",
"uuid",
] }
sysinfo = { workspace = true, features = ["disk", "system"] }
tauri = { workspace = true, features = ["unstable"], optional = true }
tempfile = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = [
"fs",
"io-util",
"macros",
"net",
"process",
"sync",
"time",
] }
tokio-util = { workspace = true, features = [
"compat",
"io",
"io-util",
"time",
] }
tracing = { workspace = true }
tracing-error = { workspace = true }
tracing-subscriber = { workspace = true, features = ["chrono", "env-filter"] }
url = { workspace = true, features = ["serde"] } url = { workspace = true, features = ["serde"] }
uuid = { workspace = true, features = ["serde", "v4"] } uuid = { workspace = true, features = ["serde", "v4"] }
zip.workspace = true whoami = { workspace = true }
async_zip = { workspace = true, features = [ zbus = { workspace = true }
"chrono", zip = { workspace = true }
"tokio-fs",
"deflate",
"bzip2",
"zstd",
"deflate64",
] }
flate2.workspace = true
tempfile.workspace = true
dashmap = { workspace = true, features = ["serde"] }
quick-xml = { workspace = true, features = ["async-tokio"] }
enumset.workspace = true
chardetng.workspace = true
encoding_rs.workspace = true
png.workspace = true
bytemuck = { workspace = true, features = ["extern_crate_alloc"] }
rgb.workspace = true
phf.workspace = true
itertools.workspace = true
derive_more = { workspace = true, features = ["display"] }
chrono = { workspace = true, features = ["serde"] }
daedalus.workspace = true
dirs.workspace = true
regex.workspace = true
sysinfo = { workspace = true, features = ["system", "disk"] }
thiserror.workspace = true
either.workspace = true
data-url.workspace = true
tracing.workspace = true
tracing-subscriber = { workspace = true, features = ["chrono", "env-filter"] }
tracing-error.workspace = true
paste.workspace = true
heck.workspace = true
tauri = { workspace = true, optional = true, features = ["unstable"] }
indicatif = { workspace = true, optional = true }
async-tungstenite = { workspace = true, features = ["tokio-runtime", "tokio-rustls-webpki-roots"] }
futures = { workspace = true, features = ["async-await", "alloc"] }
reqwest = { workspace = true, features = [
"json",
"stream",
"deflate",
"gzip",
"brotli",
"rustls-tls-webpki-roots",
"charset",
"http2",
"macos-system-configuration",
"multipart",
] }
tokio = { workspace = true, features = [
"time",
"io-util",
"net",
"sync",
"fs",
"macros",
"process",
] }
tokio-util = { workspace = true, features = ["compat", "io", "io-util", "time"] }
async-recursion.workspace = true
fs4 = { workspace = true, features = ["tokio"] }
async-walkdir.workspace = true
async-compression = { workspace = true, features = ["tokio", "gzip"] }
notify.workspace = true
notify-debouncer-mini.workspace = true
dunce.workspace = true
whoami.workspace = true
discord-rich-presence.workspace = true
p256 = { workspace = true, features = ["ecdsa"] }
rand.workspace = true
base64.workspace = true
sqlx = { workspace = true, features = [
"runtime-tokio",
"sqlite",
"macros",
"migrate",
"json",
"uuid",
] }
quartz_nbt = { workspace = true, features = ["serde"] }
hickory-resolver.workspace = true
zbus.workspace = true
ariadne.workspace = true
path-util.workspace = true
[target.'cfg(windows)'.dependencies]
winreg.workspace = true
windows = { workspace = true, features = ["Networking_Connectivity"] }
windows-core.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
cidre = { workspace = true, features = ["nw", "blocks"] }
[build-dependencies] [build-dependencies]
dotenvy.workspace = true dotenvy = { workspace = true }
dunce.workspace = true dunce = { workspace = true }
[target.'cfg(target_os = "macos")'.dependencies]
cidre = { workspace = true, features = ["blocks", "nw"] }
[target.'cfg(windows)'.dependencies]
windows = { workspace = true, features = ["Networking_Connectivity"] }
windows-core = { workspace = true }
winreg = { workspace = true }
[features] [features]
tauri = ["dep:tauri"]
cli = ["dep:indicatif"] cli = ["dep:indicatif"]
tauri = ["dep:tauri"]
[lints] [lints]
workspace = true workspace = true

View File

@@ -120,11 +120,11 @@ fn parse_server_address_inner(
let mut port = None; let mut port = None;
if !port_str.is_empty() { if !port_str.is_empty() {
if port_str.starts_with('+') { if port_str.starts_with('+') {
return Err(format!("Unparseable port number: {port_str}")); return Err(format!("Unparsable port number: {port_str}"));
} }
port = port_str.parse::<u16>().ok(); port = port_str.parse::<u16>().ok();
if port.is_none() { if port.is_none() {
return Err(format!("Unparseable port number: {port_str}")); return Err(format!("Unparsable port number: {port_str}"));
} }
} }

View File

@@ -16,14 +16,14 @@ use uuid::Uuid;
const CLI_PROGRESS_BAR_TOTAL: u64 = 1000; const CLI_PROGRESS_BAR_TOTAL: u64 = 1000;
/* /*
Events are a way we can communciate with the Tauri frontend from the Rust backend. Events are a way we can communicate with the Tauri frontend from the Rust backend.
We include a feature flag for Tauri, so that we can compile this code without Tauri. We include a feature flag for Tauri, so that we can compile this code without Tauri.
To use events, we need to do the following: To use events, we need to do the following:
1) Make sure we are using the tauri feature flag 1) Make sure we are using the tauri feature flag
2) Initialize the EventState with EventState::init() *before* initializing the theseus State 2) Initialize the EventState with EventState::init() *before* initializing the theseus State
3) Call emit_x functions to send events to the frontend 3) Call emit_x functions to send events to the frontend
For emit_loading() specifically, we need to inialize the loading bar with init_loading() first and pass the received loader in For emit_loading() specifically, we need to initialize the loading bar with init_loading() first and pass the received loader in
For example: For example:
pub async fn loading_function() -> crate::Result<()> { pub async fn loading_function() -> crate::Result<()> {
@@ -306,7 +306,7 @@ pub async fn emit_friend(payload: FriendPayload) -> crate::Result<()> {
// loading_join! macro // loading_join! macro
// loading_join!(key: Option<&LoadingBarId>, total: f64, message: Option<&str>; task1, task2, task3...) // loading_join!(key: Option<&LoadingBarId>, total: f64, message: Option<&str>; task1, task2, task3...)
// This will submit a loading event with the given message for each task as they complete // This will submit a loading event with the given message for each task as they complete
// task1, task2, task3 are async tasks that yuo want to to join on await on // task1, task2, task3 are async tasks that you want to to join on await on
// Key is the key to use for which loading bar to submit these results to- a LoadingBarId. If None, it does nothing // Key is the key to use for which loading bar to submit these results to- a LoadingBarId. If None, it does nothing
// Total is the total amount of progress that the loading bar should take up by all futures in this (will be split evenly amongst them). // Total is the total amount of progress that the loading bar should take up by all futures in this (will be split evenly amongst them).
// If message is Some(t) you will overwrite this loading bar's message with a custom one // If message is Some(t) you will overwrite this loading bar's message with a custom one

View File

@@ -24,7 +24,7 @@ pub async fn init_watcher() -> crate::Result<FileWatcher> {
tokio::task::spawn(async move { tokio::task::spawn(async move {
let span = tracing::span!(tracing::Level::INFO, "init_watcher"); let span = tracing::span!(tracing::Level::INFO, "init_watcher");
tracing::info!(parent: &span, "Initting watcher"); tracing::info!(parent: &span, "Initing watcher");
while let Some(res) = rx.recv().await { while let Some(res) = rx.recv().await {
let _span = span.enter(); let _span = span.enter();

View File

@@ -4,15 +4,15 @@ version = "0.1.0"
edition.workspace = true edition.workspace = true
[dependencies] [dependencies]
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
thiserror.workspace = true
uuid = { workspace = true, features = ["v4", "fast-rng", "serde"] }
serde_bytes.workspace = true
rand.workspace = true
either.workspace = true
chrono = { workspace = true, features = ["serde"] } chrono = { workspace = true, features = ["serde"] }
serde_cbor.workspace = true either = { workspace = true }
rand = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_bytes = { workspace = true }
serde_cbor = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
uuid = { workspace = true, features = ["fast-rng", "serde", "v4"] }
[lints] [lints]
workspace = true workspace = true

View File

@@ -1,23 +1,20 @@
[package] [package]
name = "daedalus" name = "daedalus"
version = "0.2.3" version = "0.2.3"
authors = ["Jai A <jai@modrinth.com>"]
edition.workspace = true edition.workspace = true
license = "MIT"
description = "Utilities for querying and parsing Minecraft metadata" description = "Utilities for querying and parsing Minecraft metadata"
repository = "https://github.com/modrinth/daedalus/"
include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
keywords = ["minecraft", "launcher"]
categories = ["game-development", "api-bindings"]
readme = "README.md" readme = "README.md"
repository = "https://github.com/modrinth/daedalus/"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html license = "MIT"
keywords = ["launcher", "minecraft"]
categories = ["api-bindings", "game-development"]
include = ["Cargo.toml", "LICENSE", "README.md", "src/**/*.rs"]
[dependencies] [dependencies]
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
chrono = { workspace = true, features = ["serde"] } chrono = { workspace = true, features = ["serde"] }
thiserror.workspace = true serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
thiserror = { workspace = true }
[lints] [lints]
workspace = true workspace = true

View File

@@ -50,17 +50,17 @@ const links: Stage = {
], ],
} as ButtonAction, } as ButtonAction,
{ {
id: 'links_unaccessible', id: 'links_inaccessible',
type: 'button', type: 'button',
label: 'Links are inaccessible', label: 'Links are inaccessible',
weight: 510, weight: 510,
suggestedStatus: 'flagged', suggestedStatus: 'flagged',
// Theoretically a conditional could go here to prevent overlap of misuse and unaccessible messages repeating while still allowing for a multi-select in each. // Theoretically a conditional could go here to prevent overlap of misuse and inaccessible messages repeating while still allowing for a multi-select in each.
// if links_misused was selected, send nothing. // if links_misused was selected, send nothing.
message: async () => (await import('../messages/links/not_accessible.md?raw')).default, message: async () => (await import('../messages/links/not_accessible.md?raw')).default,
enablesActions: [ enablesActions: [
{ {
id: 'links_unaccessible_options', id: 'links_inaccessible_options',
type: 'multi-select-chips', type: 'multi-select-chips',
label: 'Warn of inaccessible link?', label: 'Warn of inaccessible link?',
shouldShow: (project) => Boolean(project.source_url || project.discord_url), shouldShow: (project) => Boolean(project.source_url || project.discord_url),

View File

@@ -0,0 +1,134 @@
#![doc = include_str!("../README.md")]
mod account;
mod error;
mod organization;
mod payout;
mod util;
pub use {account::*, error::*, organization::*, payout::*};
use rust_decimal::Decimal;
use secrecy::SecretString;
use serde::{Deserialize, Serialize};
use std::ops::Deref;
use uuid::Uuid;
pub const API_URL: &str = "https://api.muralpay.com";
pub const SANDBOX_API_URL: &str = "https://api-staging.muralpay.com";
#[derive(Debug)]
pub struct MuralPay {
pub http: reqwest::Client,
pub api_url: String,
pub api_key: SecretString,
pub transfer_api_key: Option<SecretString>,
}
impl MuralPay {
pub fn new(
api_url: impl Into<String>,
api_key: impl Into<SecretString>,
transfer_api_key: Option<impl Into<SecretString>>,
) -> Self {
Self {
http: reqwest::Client::new(),
api_url: api_url.into(),
api_key: api_key.into(),
transfer_api_key: transfer_api_key.map(Into::into),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Blockchain {
Ethereum,
Polygon,
Base,
Celo,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING-KEBAB-CASE")]
pub enum CurrencyCode {
Usd,
Cop,
Ars,
Eur,
Mxn,
Brl,
Clp,
Pen,
Bob,
Crc,
Zar,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum FiatAndRailCode {
Usd,
Cop,
Ars,
Eur,
Mxn,
Brl,
Clp,
Pen,
Bob,
Crc,
Zar,
UsdPeru,
UsdChina,
UsdPanama,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WalletDetails {
pub blockchain: Blockchain,
pub wallet_address: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenAmount {
pub token_amount: Decimal,
pub token_symbol: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiatAmount {
pub fiat_amount: Decimal,
pub fiat_currency_code: CurrencyCode,
}
#[derive(Debug, Clone, Default)]
pub struct SearchParams<Id> {
pub limit: Option<u64>,
pub next_id: Option<Id>,
}
impl<Id: Deref<Target = Uuid> + Clone> SearchParams<Id> {
pub fn to_query(&self) -> Vec<(&'static str, String)> {
[
self.limit.map(|limit| ("limit", limit.to_string())),
self.next_id
.clone()
.map(|id| ("nextId", id.hyphenated().to_string())),
]
.into_iter()
.flatten()
.collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SearchResponse<Id, T> {
pub total: u64,
pub next_id: Option<Id>,
pub results: Vec<T>,
}

View File

@@ -3,10 +3,10 @@ name = "path-util"
edition.workspace = true edition.workspace = true
[dependencies] [dependencies]
typed-path.workspace = true derive_more = { workspace = true, features = ["deref", "display"] }
serde.workspace = true itertools = { workspace = true }
derive_more = { workspace = true, features = ["display", "deref"] } serde = { workspace = true }
itertools.workspace = true typed-path = { workspace = true }
[lints] [lints]
workspace = true workspace = true

View File

@@ -1,2 +1,3 @@
[toolchain] [toolchain]
channel = "1.89.0" channel = "1.89.0"
profile = "default"

2
tombi.toml Normal file
View File

@@ -0,0 +1,2 @@
[files]
exclude = ["target/", "**/node_modules/"]