Skip to content

Commit ed5e050

Browse files
Merge branch 'main' into fix/styling-issue
2 parents 5d677cc + c52d5e9 commit ed5e050

File tree

13 files changed

+229
-51
lines changed

13 files changed

+229
-51
lines changed

apps/app/src/main.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
)]
55

66
use native_dialog::{MessageDialog, MessageType};
7+
use std::env;
78
use tauri::{Listener, Manager};
89
use theseus::prelude::*;
910

@@ -29,7 +30,12 @@ async fn initialize_state(app: tauri::AppHandle) -> api::Result<()> {
2930
theseus::EventState::init(app.clone()).await?;
3031

3132
#[cfg(feature = "updater")]
32-
{
33+
'updater: {
34+
if env::var("MODRINTH_EXTERNAL_UPDATE_PROVIDER").is_ok() {
35+
State::init().await?;
36+
break 'updater;
37+
}
38+
3339
use tauri_plugin_updater::UpdaterExt;
3440

3541
let updater = app.updater_builder().build()?;

apps/frontend/src/components/ui/AdPlaceholder.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ import { ChevronRightIcon } from "@modrinth/assets";
2222
2323
useHead({
2424
script: [
25-
{
26-
// Clean.io
27-
src: "https://cadmus.script.ac/d14pdm1b7fi5kh/script.js",
28-
},
25+
// {
26+
// // Clean.io
27+
// src: "https://cadmus.script.ac/d14pdm1b7fi5kh/script.js",
28+
// },
2929
{
3030
// Aditude
3131
src: "https://dn0qt3r0xannq.cloudfront.net/modrinth-7JfmkEIXEp/modrinth-longform/prebid-load.js",

apps/frontend/src/components/ui/servers/ServerListing.vue

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,15 @@
6161
Your server's hardware is currently being upgraded and will be back online shortly.
6262
</div>
6363
<div
64-
v-else-if="status === 'suspended'"
64+
v-if="status === 'suspended' && suspension_reason === 'support'"
65+
class="relative -mt-4 flex w-full flex-row items-center gap-2 rounded-b-3xl bg-bg-blue p-4 text-sm font-bold text-contrast"
66+
>
67+
<HammerIcon />
68+
You recently requested support for your server and we are actively working on it. It will be
69+
back online shortly.
70+
</div>
71+
<div
72+
v-else-if="status === 'suspended' && suspension_reason !== 'upgrading'"
6573
class="relative -mt-4 flex w-full flex-row items-center gap-2 rounded-b-3xl bg-bg-red p-4 text-sm font-bold text-contrast"
6674
>
6775
<UiServersIconsPanelErrorIcon class="!size-5" />
@@ -72,7 +80,7 @@
7280
</template>
7381

7482
<script setup lang="ts">
75-
import { ChevronRightIcon, LockIcon } from "@modrinth/assets";
83+
import { ChevronRightIcon, HammerIcon, LockIcon } from "@modrinth/assets";
7684
import type { Project, Server } from "~/types/servers";
7785
7886
const props = defineProps<Partial<Server>>();

apps/frontend/src/composables/pyroServers.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,10 @@ async function PyroFetch<T>(path: string, options: PyroFetchOptions = {}): Promi
6767
});
6868
return response;
6969
} catch (error) {
70-
console.error("[PYROSERVERS]:", error);
70+
console.error("[PyroServers/PyroFetch]:", error);
7171
if (error instanceof FetchError) {
7272
const statusCode = error.response?.status;
73-
const statusText = error.response?.statusText || "Unknown error";
73+
const statusText = error.response?.statusText || "[no status text available]";
7474
const errorMessages: { [key: number]: string } = {
7575
400: "Bad Request",
7676
401: "Unauthorized",
@@ -80,15 +80,16 @@ async function PyroFetch<T>(path: string, options: PyroFetchOptions = {}): Promi
8080
429: "Too Many Requests",
8181
500: "Internal Server Error",
8282
502: "Bad Gateway",
83+
503: "Service Unavailable",
8384
};
8485
const message =
8586
statusCode && statusCode in errorMessages
8687
? errorMessages[statusCode]
87-
: `HTTP Error: ${statusCode || "unknown"} ${statusText}`;
88-
throw new PyroFetchError(`[PYROSERVERS][PYRO] ${message}`, statusCode, error);
88+
: `HTTP Error: ${statusCode || "[unhandled status code]"} ${statusText}`;
89+
throw new PyroFetchError(`[PyroServers/PyroFetch] ${message}`, statusCode, error);
8990
}
9091
throw new PyroFetchError(
91-
"[PYROSERVERS][PYRO] An unexpected error occurred during the fetch operation.",
92+
"[PyroServers/PyroFetch] An unexpected error occurred during the fetch operation.",
9293
undefined,
9394
error as Error,
9495
);
@@ -168,7 +169,15 @@ interface General {
168169
backup_quota: number;
169170
used_backup_quota: number;
170171
status: string;
171-
suspension_reason: string;
172+
suspension_reason:
173+
| "moderated"
174+
| "paymentfailed"
175+
| "cancelled"
176+
| "other"
177+
| "transferring"
178+
| "upgrading"
179+
| "support"
180+
| (string & {});
172181
loader: string;
173182
loader_version: string;
174183
mc_version: string;

apps/frontend/src/pages/servers/manage/[id].vue

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,26 @@
1919
</div>
2020
</div>
2121
<div
22-
v-else-if="serverData?.status === 'suspended'"
22+
v-if="serverData?.status === 'suspended' && serverData.suspension_reason === 'support'"
23+
class="flex min-h-[calc(100vh-4rem)] items-center justify-center text-contrast"
24+
>
25+
<div class="flex max-w-lg flex-col items-center rounded-3xl bg-bg-raised p-6 shadow-xl">
26+
<div class="flex flex-col items-center text-center">
27+
<div class="flex flex-col items-center gap-4">
28+
<div class="grid place-content-center rounded-full bg-bg-blue p-4">
29+
<TransferIcon class="size-12 text-blue" />
30+
</div>
31+
<h1 class="m-0 mb-2 w-fit text-4xl font-bold">We're working on your server</h1>
32+
</div>
33+
<p class="text-lg text-secondary">
34+
You recently contacted Modrinth Support, and we're actively working on your server. It
35+
will be back online shortly.
36+
</p>
37+
</div>
38+
</div>
39+
</div>
40+
<div
41+
v-else-if="serverData?.status === 'suspended' && serverData.suspension_reason !== 'upgrading'"
2342
class="flex min-h-[calc(100vh-4rem)] items-center justify-center text-contrast"
2443
>
2544
<div class="flex max-w-lg flex-col items-center rounded-3xl bg-bg-raised p-6 shadow-xl">
@@ -69,6 +88,58 @@
6988
</ButtonStyled>
7089
</div>
7190
</div>
91+
<div
92+
v-else-if="server.error && server.error.message.includes('Service Unavailable')"
93+
class="flex min-h-[calc(100vh-4rem)] items-center justify-center text-contrast"
94+
>
95+
<div class="flex max-w-lg flex-col items-center rounded-3xl bg-bg-raised p-6 shadow-xl">
96+
<div class="flex flex-col items-center text-center">
97+
<div class="flex flex-col items-center gap-4">
98+
<div class="grid place-content-center rounded-full bg-bg-red p-4">
99+
<PanelErrorIcon class="size-12 text-red" />
100+
</div>
101+
<h1 class="m-0 mb-4 w-fit text-4xl font-bold">Server Node Unavailable</h1>
102+
</div>
103+
<p class="m-0 mb-4 leading-[170%] text-secondary">
104+
Your server's node, where your Modrinth Server is physically hosted, is experiencing
105+
issues. We are working with our datacenter to resolve the issue as quickly as possible.
106+
</p>
107+
<p class="m-0 mb-4 leading-[170%] text-secondary">
108+
Your data is safe and will not be lost, and your server will be back online as soon as
109+
the issue is resolved.
110+
</p>
111+
<p class="m-0 mb-4 leading-[170%] text-secondary">
112+
For updates, please join the Modrinth Discord or contact Modrinth Support via the chat
113+
bubble in the bottom right corner and we'll be happy to help.
114+
</p>
115+
116+
<div class="flex flex-col gap-2">
117+
<UiCopyCode :text="'Server ID: ' + server.serverId" />
118+
<UiCopyCode :text="'Node: ' + server.general?.datacenter" />
119+
</div>
120+
</div>
121+
<ButtonStyled
122+
size="large"
123+
color="standard"
124+
@click="
125+
() =>
126+
navigateTo('https://discord.modrinth.com', {
127+
external: true,
128+
})
129+
"
130+
>
131+
<button class="mt-6 !w-full">Join Modrinth Discord</button>
132+
</ButtonStyled>
133+
<ButtonStyled
134+
:disabled="formattedTime !== '00'"
135+
size="large"
136+
color="standard"
137+
@click="() => reloadNuxtApp()"
138+
>
139+
<button class="mt-3 !w-full">Reload</button>
140+
</ButtonStyled>
141+
</div>
142+
</div>
72143
<div
73144
v-else-if="server.error"
74145
class="flex min-h-[calc(100vh-4rem)] items-center justify-center text-contrast"
@@ -324,6 +395,7 @@ import { Intercom, shutdown } from "@intercom/messenger-js-sdk";
324395
import { reloadNuxtApp } from "#app";
325396
import type { ServerState, Stats, WSEvent, WSInstallationResultEvent } from "~/types/servers";
326397
import { usePyroConsole } from "~/store/console.ts";
398+
import PanelErrorIcon from "~/components/ui/servers/icons/PanelErrorIcon.vue";
327399
328400
const socket = ref<WebSocket | null>(null);
329401
const isReconnecting = ref(false);

apps/labrinth/.env

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,7 @@ ADITUDE_API_KEY=none
112112
PYRO_API_KEY=none
113113

114114
BREX_API_URL=https://platform.brexapis.com/v2/
115-
BREX_API_KEY=none
115+
BREX_API_KEY=none
116+
117+
DELPHI_URL=none
118+
DELPHI_SLACK_WEBHOOK=none

apps/labrinth/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,10 @@ pub fn check_env_vars() -> bool {
489489

490490
failed |= check_var::<String>("PYRO_API_KEY");
491491

492+
failed |= check_var::<String>("BREX_API_URL");
492493
failed |= check_var::<String>("BREX_API_KEY");
493494

495+
failed |= check_var::<String>("DELPHI_URL");
496+
494497
failed
495498
}

apps/labrinth/src/queue/moderation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::io::{Cursor, Read};
1818
use std::time::Duration;
1919
use zip::ZipArchive;
2020

21-
const AUTOMOD_ID: i64 = 0;
21+
pub const AUTOMOD_ID: i64 = 0;
2222

2323
pub struct ModerationMessages {
2424
pub messages: Vec<ModerationMessage>,

apps/labrinth/src/routes/internal/admin.rs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
use crate::auth::validate::get_user_record_from_bearer_token;
2+
use crate::database::models::thread_item::ThreadMessageBuilder;
23
use crate::database::redis::RedisPool;
34
use crate::models::analytics::Download;
45
use crate::models::ids::ProjectId;
56
use crate::models::pats::Scopes;
7+
use crate::models::threads::MessageBody;
68
use crate::queue::analytics::AnalyticsQueue;
79
use crate::queue::maxmind::MaxMindIndexer;
10+
use crate::queue::moderation::AUTOMOD_ID;
811
use crate::queue::payouts::PayoutsQueue;
912
use crate::queue::session::AuthQueue;
1013
use crate::routes::ApiError;
@@ -17,13 +20,15 @@ use sqlx::PgPool;
1720
use std::collections::HashMap;
1821
use std::net::Ipv4Addr;
1922
use std::sync::Arc;
23+
use log::info;
2024

2125
pub fn config(cfg: &mut web::ServiceConfig) {
2226
cfg.service(
2327
web::scope("admin")
2428
.service(count_download)
2529
.service(force_reindex)
26-
.service(get_balances),
30+
.service(get_balances)
31+
.service(delphi_result_ingest),
2732
);
2833
}
2934

@@ -178,3 +183,75 @@ pub async fn get_balances(
178183
"tremendous": tremendous,
179184
})))
180185
}
186+
187+
#[derive(Deserialize)]
188+
pub struct DelphiIngest {
189+
pub url: String,
190+
pub project_id: crate::models::ids::ProjectId,
191+
pub version_id: crate::models::ids::VersionId,
192+
pub issues: Vec<String>,
193+
}
194+
195+
#[post("/_delphi", guard = "admin_key_guard")]
196+
pub async fn delphi_result_ingest(
197+
pool: web::Data<PgPool>,
198+
redis: web::Data<RedisPool>,
199+
body: web::Json<DelphiIngest>,
200+
) -> Result<HttpResponse, ApiError> {
201+
if body.issues.is_empty() {
202+
info!("No issues found for file {}", body.url);
203+
return Ok(HttpResponse::NoContent().finish());
204+
}
205+
206+
let webhook_url = dotenvy::var("DELPHI_SLACK_WEBHOOK")?;
207+
208+
let project = crate::database::models::Project::get_id(
209+
body.project_id.into(),
210+
&**pool,
211+
&redis,
212+
)
213+
.await?
214+
.ok_or_else(|| {
215+
ApiError::InvalidInput(format!(
216+
"Project {} does not exist",
217+
body.project_id
218+
))
219+
})?;
220+
221+
crate::util::webhook::send_slack_webhook(
222+
body.project_id,
223+
&pool,
224+
&redis,
225+
webhook_url,
226+
Some(format!(
227+
"Suspicious traces found at {}. Traces: {}",
228+
body.url,
229+
body.issues.join(", ")
230+
)),
231+
)
232+
.await
233+
.ok();
234+
235+
let mut transaction = pool.begin().await?;
236+
ThreadMessageBuilder {
237+
author_id: Some(crate::database::models::UserId(AUTOMOD_ID)),
238+
body: MessageBody::Text {
239+
body: format!(
240+
"WSR; Suspicious traces found for version_id {}. Traces: {}",
241+
body.version_id,
242+
body.issues.join(", ")
243+
),
244+
private: true,
245+
replying_to: None,
246+
associated_images: vec![],
247+
},
248+
thread_id: project.thread_id,
249+
hide_identity: false,
250+
}
251+
.insert(&mut transaction)
252+
.await?;
253+
254+
transaction.commit().await?;
255+
256+
Ok(HttpResponse::NoContent().finish())
257+
}

apps/labrinth/src/routes/v2/projects.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,6 @@ pub async fn project_search(
6161
let facets: Option<Vec<Vec<String>>> = if let Some(facets) = info.facets {
6262
let facets = serde_json::from_str::<Vec<Vec<String>>>(&facets)?;
6363

64-
// These loaders specifically used to be combined with 'mod' to be a plugin, but now
65-
// they are their own loader type. We will convert 'mod' to 'mod' OR 'plugin'
66-
// as it essentially was before.
67-
let facets = v2_reroute::convert_plugin_loader_facets_v3(facets);
68-
6964
Some(
7065
facets
7166
.into_iter()

0 commit comments

Comments
 (0)