From 24295ea482ed95856beca1002c489fd204040c12 Mon Sep 17 00:00:00 2001 From: Jai A Date: Fri, 17 Jan 2025 08:53:54 -0800 Subject: [PATCH 001/104] fix version uploading --- .../migrations/20250117013050_missing-primary-keys.sql | 2 +- apps/labrinth/src/database/models/loader_fields.rs | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/labrinth/migrations/20250117013050_missing-primary-keys.sql b/apps/labrinth/migrations/20250117013050_missing-primary-keys.sql index 1a8588e23..2054cf49f 100644 --- a/apps/labrinth/migrations/20250117013050_missing-primary-keys.sql +++ b/apps/labrinth/migrations/20250117013050_missing-primary-keys.sql @@ -20,7 +20,7 @@ WHERE ctid IN ( FROM CTE WHERE row_num > 1 ); - +x ALTER TABLE version_fields ADD PRIMARY KEY (version_id, field_id, enum_value); diff --git a/apps/labrinth/src/database/models/loader_fields.rs b/apps/labrinth/src/database/models/loader_fields.rs index 70c74150e..292c6e12a 100644 --- a/apps/labrinth/src/database/models/loader_fields.rs +++ b/apps/labrinth/src/database/models/loader_fields.rs @@ -757,7 +757,12 @@ impl VersionField { l.field_id.0, l.version_id.0, l.int_value, - l.enum_value.as_ref().map(|e| e.0), + if let Some(enum_value) = l.enum_value.as_ref().map(|e| e.0) + { + enum_value + } else { + -1 + }, l.string_value.clone(), ) }) @@ -772,7 +777,7 @@ impl VersionField { &version_ids[..], &int_values[..] as &[Option], &string_values[..] as &[Option], - &enum_values[..] as &[Option] + &enum_values[..] as &[i32] ) .execute(&mut **transaction) .await?; From d7814e115d5324ee59a9ee19becbb8de4da932f1 Mon Sep 17 00:00:00 2001 From: Jai A Date: Fri, 17 Jan 2025 09:05:42 -0800 Subject: [PATCH 002/104] fix migration typo --- .../labrinth/migrations/20250117013050_missing-primary-keys.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/labrinth/migrations/20250117013050_missing-primary-keys.sql b/apps/labrinth/migrations/20250117013050_missing-primary-keys.sql index 2054cf49f..1a8588e23 100644 --- a/apps/labrinth/migrations/20250117013050_missing-primary-keys.sql +++ b/apps/labrinth/migrations/20250117013050_missing-primary-keys.sql @@ -20,7 +20,7 @@ WHERE ctid IN ( FROM CTE WHERE row_num > 1 ); -x + ALTER TABLE version_fields ADD PRIMARY KEY (version_id, field_id, enum_value); From 75b357a0694601ab2c94f5bf96272ec9dc04aead Mon Sep 17 00:00:00 2001 From: Jai Agrawal <18202329+Geometrically@users.noreply.github.com> Date: Fri, 17 Jan 2025 16:41:49 -0800 Subject: [PATCH 003/104] Staff support dashboard routes (#3160) * Staff support dashboard routes * Fix clippy --- .../frontend/src/pages/admin/billing/[id].vue | 210 ++++++++++++++++++ .../src/pages/settings/billing/charges.vue | 18 +- apps/frontend/src/pages/user/[id].vue | 89 +++++++- .../src/database/models/loader_fields.rs | 7 +- apps/labrinth/src/routes/internal/billing.rs | 38 +++- apps/labrinth/src/routes/v2/users.rs | 4 +- .../src/routes/v3/project_creation.rs | 4 - apps/labrinth/src/routes/v3/users.rs | 21 +- .../src/routes/v3/version_creation.rs | 16 +- 9 files changed, 371 insertions(+), 36 deletions(-) create mode 100644 apps/frontend/src/pages/admin/billing/[id].vue diff --git a/apps/frontend/src/pages/admin/billing/[id].vue b/apps/frontend/src/pages/admin/billing/[id].vue new file mode 100644 index 000000000..c5c72b5d4 --- /dev/null +++ b/apps/frontend/src/pages/admin/billing/[id].vue @@ -0,0 +1,210 @@ + + diff --git a/apps/frontend/src/pages/settings/billing/charges.vue b/apps/frontend/src/pages/settings/billing/charges.vue index c1195d2a4..a4c8eddc1 100644 --- a/apps/frontend/src/pages/settings/billing/charges.vue +++ b/apps/frontend/src/pages/settings/billing/charges.vue @@ -25,7 +25,7 @@ ⋅ - {{ formatPrice(charge.amount, charge.currency_code) }} + {{ formatPrice(vintl.locale, charge.amount, charge.currency_code) }}
@@ -39,6 +39,7 @@ diff --git a/apps/frontend/src/pages/user/[id].vue b/apps/frontend/src/pages/user/[id].vue index fe48a8485..14c1af888 100644 --- a/apps/frontend/src/pages/user/[id].vue +++ b/apps/frontend/src/pages/user/[id].vue @@ -2,6 +2,57 @@
+ +
+
+ Email +
+ + {{ user.email }} + + + +
+
+ +
+ Auth providers + {{ user.auth_providers.join(", ") }} +
+ +
+ Payment methods + + + + + +
+ +
+ Has password + + {{ user.has_password ? "Yes" : "No" }} + +
+ +
+ Has TOTP + + {{ user.has_totp ? "Yes" : "No" }} + +
+
+
- @@ -89,13 +92,15 @@ :type="charge.status" /> ⋅ + {{ charge.type }} + ⋅ {{ $dayjs(charge.due).format("YYYY-MM-DD") }} ⋅ {{ formatPrice(vintl.locale, charge.amount, charge.currency_code) }}
@@ -322,7 +326,7 @@ const props = withDefaults( * @deprecated Use `ranges` instead */ resoloutions?: Record; - ranges?: Record; + ranges?: RangeObject[]; personal?: boolean; }>(), { @@ -335,12 +339,6 @@ const props = withDefaults( const projects = ref(props.projects || []); -const selectableRanges = Object.entries(props.ranges).map(([duration, extra]) => ({ - label: typeof extra === "string" ? extra : extra[0], - value: Number(duration), - res: typeof extra === "string" ? Number(duration) : extra[1], -})); - // const selectedChart = ref('downloads') const selectedChart = computed({ get: () => { @@ -413,33 +411,78 @@ const isUsingProjectColors = computed({ }, }); +const startDate = ref(dayjs().startOf("day")); +const endDate = ref(dayjs().endOf("day")); +const timeResolution = ref(30); + +onBeforeMount(() => { + // Load cached data and range from localStorage - cache. + if (import.meta.client) { + const rangeLabel = localStorage.getItem("analyticsSelectedRange"); + if (rangeLabel) { + const range = props.ranges.find((r) => r.getLabel([dayjs(), dayjs()]) === rangeLabel)!; + + if (range !== undefined) { + internalRange.value = range; + const ranges = range.getDates(dayjs()); + timeResolution.value = range.timeResolution; + startDate.value = ranges.startDate; + endDate.value = ranges.endDate; + } + } + } +}); + +onMounted(() => { + if (internalRange.value === null) { + internalRange.value = props.ranges.find( + (r) => r.getLabel([dayjs(), dayjs()]) === "Previous 30 days", + )!; + } + + const ranges = selectedRange.value.getDates(dayjs()); + startDate.value = ranges.startDate; + endDate.value = ranges.endDate; + timeResolution.value = selectedRange.value.timeResolution; +}); + +const internalRange: Ref = ref(null as unknown as RangeObject); + +const selectedRange = computed({ + get: () => { + return internalRange.value; + }, + set: (newRange) => { + const ranges = newRange.getDates(dayjs()); + startDate.value = ranges.startDate; + endDate.value = ranges.endDate; + timeResolution.value = newRange.timeResolution; + + internalRange.value = newRange; + + if (import.meta.client) { + localStorage.setItem( + "analyticsSelectedRange", + internalRange.value?.getLabel([dayjs(), dayjs()]) ?? "Previous 30 days", + ); + } + }, +}); + const analytics = useFetchAllAnalytics( resetCharts, projects, selectedDisplayProjects, props.personal, + startDate, + endDate, + timeResolution, ); -const { startDate, endDate, timeRange, timeResolution } = analytics; - -const selectedRange = computed({ - get: () => { - return ( - selectableRanges.find((option) => option.value === timeRange.value) || { - label: "Custom", - value: timeRange.value, - } - ); - }, - set: (newRange: { label: string; value: number; res?: number }) => { - timeRange.value = newRange.value; - startDate.value = Date.now() - timeRange.value * 60 * 1000; - endDate.value = Date.now(); - - if (newRange?.res) { - timeResolution.value = newRange.res; - } - }, +const formattedCategorySubtitle = computed(() => { + return ( + selectedRange.value?.getLabel([dayjs(startDate.value), dayjs(endDate.value)]) ?? "Loading..." + ); }); const selectedDataSet = computed(() => { @@ -484,6 +527,9 @@ const onToggleColors = () => { diff --git a/packages/ui/src/components/base/MarkdownEditor.vue b/packages/ui/src/components/base/MarkdownEditor.vue index 9c6d57032..59a8746a1 100644 --- a/packages/ui/src/components/base/MarkdownEditor.vue +++ b/packages/ui/src/components/base/MarkdownEditor.vue @@ -300,8 +300,8 @@ import Chips from './Chips.vue' const props = withDefaults( defineProps<{ modelValue: string - disabled: boolean - headingButtons: boolean + disabled?: boolean + headingButtons?: boolean /** * @param file The file to upload * @throws If the file is invalid or the upload fails @@ -948,4 +948,8 @@ function openVideoModal() { pointer-events: none; cursor: not-allowed; } + +:deep(.cm-content) { + overflow: auto; +} diff --git a/packages/ui/src/components/base/RadialHeader.vue b/packages/ui/src/components/base/RadialHeader.vue new file mode 100644 index 000000000..89e80b67b --- /dev/null +++ b/packages/ui/src/components/base/RadialHeader.vue @@ -0,0 +1,49 @@ + + + diff --git a/packages/ui/src/components/base/RadioButtons.vue b/packages/ui/src/components/base/RadioButtons.vue new file mode 100644 index 000000000..fd384c4aa --- /dev/null +++ b/packages/ui/src/components/base/RadioButtons.vue @@ -0,0 +1,48 @@ + + diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 8fe546bc9..bf53d7aac 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -25,6 +25,8 @@ export { default as Pagination } from './base/Pagination.vue' export { default as PopoutMenu } from './base/PopoutMenu.vue' export { default as PreviewSelectButton } from './base/PreviewSelectButton.vue' export { default as ProjectCard } from './base/ProjectCard.vue' +export { default as RadialHeader } from './base/RadialHeader.vue' +export { default as RadioButtons } from './base/RadioButtons.vue' export { default as ScrollablePanel } from './base/ScrollablePanel.vue' export { default as SimpleBadge } from './base/SimpleBadge.vue' export { default as Slider } from './base/Slider.vue' diff --git a/packages/utils/types.ts b/packages/utils/types.ts index 3fe5dbec1..76962e9e5 100644 --- a/packages/utils/types.ts +++ b/packages/utils/types.ts @@ -1,7 +1,7 @@ export const BASE62_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' export type Base62Char = (typeof BASE62_CHARS)[number] -export type ModrinthId = `${Base62Char}`[] +export type ModrinthId = string export type Environment = 'required' | 'optional' | 'unsupported' | 'unknown' @@ -241,3 +241,15 @@ export interface TeamMember { payouts_split: number ordering: number } + +export type Report = { + id: ModrinthId + item_id: ModrinthId + item_type: 'project' | 'version' | 'user' + report_type: string + reporter: ModrinthId + thread_id: ModrinthId + closed: boolean + created: string + body: string +} From 5bc63a5ad31508bdb1dff94eefe303b9df30ff2f Mon Sep 17 00:00:00 2001 From: Jai A Date: Thu, 6 Feb 2025 10:02:44 -0800 Subject: [PATCH 024/104] Ads window system blocker --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- apps/app-frontend/package.json | 2 +- apps/app/Cargo.toml | 2 +- apps/app/tauri.conf.json | 2 +- packages/app-lib/Cargo.toml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8dffdfd5a..02385145a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8870,7 +8870,7 @@ dependencies = [ [[package]] name = "theseus" -version = "0.9.2" +version = "0.9.3" dependencies = [ "async-recursion", "async-tungstenite", @@ -8921,7 +8921,7 @@ dependencies = [ [[package]] name = "theseus_gui" -version = "0.9.2" +version = "0.9.3" dependencies = [ "chrono", "cocoa 0.25.0", @@ -10780,7 +10780,7 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" version = "0.47.2" -source = "git+https://github.com/modrinth/wry?rev=cdbf938#cdbf9384263db4e692e22dcf9bf6085e334a10f3" +source = "git+https://github.com/modrinth/wry?rev=51907c6#51907c61541f2e389856f07773a32bac04f9a843" dependencies = [ "base64 0.22.1", "block2", diff --git a/Cargo.toml b/Cargo.toml index 06487ac14..c55a6fc2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,4 @@ strip = true # Remove debug symbols opt-level = 3 [patch.crates-io] -wry = { git = "https://github.com/modrinth/wry", rev ="cdbf938" } \ No newline at end of file +wry = { git = "https://github.com/modrinth/wry", rev = "51907c6" } \ No newline at end of file diff --git a/apps/app-frontend/package.json b/apps/app-frontend/package.json index f39380631..fe9390f97 100644 --- a/apps/app-frontend/package.json +++ b/apps/app-frontend/package.json @@ -1,7 +1,7 @@ { "name": "@modrinth/app-frontend", "private": true, - "version": "0.9.2", + "version": "0.9.3", "type": "module", "scripts": { "dev": "vite", diff --git a/apps/app/Cargo.toml b/apps/app/Cargo.toml index d4c4d1e92..0e3bc20b3 100644 --- a/apps/app/Cargo.toml +++ b/apps/app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "theseus_gui" -version = "0.9.2" +version = "0.9.3" description = "The Modrinth App is a desktop application for managing your Minecraft mods" license = "GPL-3.0-only" repository = "https://github.com/modrinth/code/apps/app/" diff --git a/apps/app/tauri.conf.json b/apps/app/tauri.conf.json index 37c5c20e5..24eb3bd41 100644 --- a/apps/app/tauri.conf.json +++ b/apps/app/tauri.conf.json @@ -44,7 +44,7 @@ ] }, "productName": "Modrinth App", - "version": "0.9.2", + "version": "0.9.3", "mainBinaryName": "Modrinth App", "identifier": "ModrinthApp", "plugins": { diff --git a/packages/app-lib/Cargo.toml b/packages/app-lib/Cargo.toml index dcf0b6ae7..4e2d83750 100644 --- a/packages/app-lib/Cargo.toml +++ b/packages/app-lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "theseus" -version = "0.9.2" +version = "0.9.3" authors = ["Jai A "] edition = "2021" From acc379d14d71d492b32c983ffbe75abefe1ee41d Mon Sep 17 00:00:00 2001 From: Jai A Date: Thu, 6 Feb 2025 10:20:58 -0800 Subject: [PATCH 025/104] Add clean.io to app frames --- apps/frontend/src/public/promo-frame.html | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/frontend/src/public/promo-frame.html b/apps/frontend/src/public/promo-frame.html index 43adfb776..2dc4fc32f 100644 --- a/apps/frontend/src/public/promo-frame.html +++ b/apps/frontend/src/public/promo-frame.html @@ -5,6 +5,7 @@ Modrinth App Ad + diff --git a/apps/frontend/src/components/ui/servers/TeleportDropdownMenu.vue b/apps/frontend/src/components/ui/servers/TeleportDropdownMenu.vue index 4c799454a..13af59e5f 100644 --- a/apps/frontend/src/components/ui/servers/TeleportDropdownMenu.vue +++ b/apps/frontend/src/components/ui/servers/TeleportDropdownMenu.vue @@ -227,7 +227,7 @@ const radioValue = computed({ }); const triggerClasses = computed(() => ({ - "cursor-not-allowed opacity-50 grayscale": props.disabled, + "!cursor-not-allowed opacity-50 grayscale": props.disabled, "rounded-b-none": dropdownVisible.value && !isRenderingUp.value && !props.disabled, "rounded-t-none": dropdownVisible.value && isRenderingUp.value && !props.disabled, })); diff --git a/apps/frontend/src/composables/pyroServers.ts b/apps/frontend/src/composables/pyroServers.ts index 74f76e3a5..930a22270 100644 --- a/apps/frontend/src/composables/pyroServers.ts +++ b/apps/frontend/src/composables/pyroServers.ts @@ -252,7 +252,7 @@ export interface DirectoryResponse { current?: number; } -type ContentType = "Mod" | "Plugin"; +type ContentType = "mod" | "plugin"; const constructServerProperties = (properties: any): string => { let fileContent = `#Minecraft server properties\n#${new Date().toUTCString()}\n`; @@ -519,8 +519,8 @@ const installContent = async (contentType: ContentType, projectId: string, versi await PyroFetch(`servers/${internalServerRefrence.value.serverId}/mods`, { method: "POST", body: { - install_as: contentType, rinth_ids: { project_id: projectId, version_id: versionId }, + install_as: contentType, }, }); } catch (error) { @@ -529,13 +529,12 @@ const installContent = async (contentType: ContentType, projectId: string, versi } }; -const removeContent = async (contentType: ContentType, contentId: string) => { +const removeContent = async (path: string) => { try { await PyroFetch(`servers/${internalServerRefrence.value.serverId}/deleteMod`, { method: "POST", body: { - install_as: contentType, - path: contentId, + path, }, }); } catch (error) { @@ -544,15 +543,11 @@ const removeContent = async (contentType: ContentType, contentId: string) => { } }; -const reinstallContent = async ( - contentType: ContentType, - contentId: string, - newContentId: string, -) => { +const reinstallContent = async (replace: string, projectId: string, versionId: string) => { try { - await PyroFetch(`servers/${internalServerRefrence.value.serverId}/mods/${contentId}`, { - method: "PUT", - body: { install_as: contentType, version_id: newContentId }, + await PyroFetch(`servers/${internalServerRefrence.value.serverId}/mods/update`, { + method: "POST", + body: { replace, project_id: projectId, version_id: versionId }, }); } catch (error) { console.error("Error reinstalling mod:", error); @@ -1160,18 +1155,17 @@ type ContentFunctions = { /** * Removes a mod from a server. - * @param contentType - The type of content to remove. - * @param contentId - The ID of the content. + * @param path - The path of the mod file. */ - remove: (contentType: ContentType, contentId: string) => Promise; + remove: (path: string) => Promise; /** * Reinstalls a mod to a server. - * @param contentType - The type of content to reinstall. - * @param contentId - The ID of the content. - * @param newContentId - The ID of the new version. + * @param replace - The path of the mod to replace. + * @param projectId - The ID of the content. + * @param versionId - The ID of the new version. */ - reinstall: (contentType: ContentType, contentId: string, newContentId: string) => Promise; + reinstall: (replace: string, projectId: string, versionId: string) => Promise; }; type BackupFunctions = { diff --git a/apps/frontend/src/pages/servers/manage/[id]/content/index.vue b/apps/frontend/src/pages/servers/manage/[id]/content/index.vue index 8161fa97c..1dc947f35 100644 --- a/apps/frontend/src/pages/servers/manage/[id]/content/index.vue +++ b/apps/frontend/src/pages/servers/manage/[id]/content/index.vue @@ -1,57 +1,14 @@